// Copyright 2023, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! IRemotelyProvisionedComponent HAL implementation.

use crate::rkpvm;
use android_hardware_security_rkp::aidl::android::hardware::security::keymint::{
    DeviceInfo::DeviceInfo,
    IRemotelyProvisionedComponent::{
        BnRemotelyProvisionedComponent, IRemotelyProvisionedComponent, STATUS_FAILED,
        STATUS_INVALID_MAC, STATUS_REMOVED,
    },
    MacedPublicKey::MacedPublicKey,
    ProtectedData::ProtectedData,
    RpcHardwareInfo::{RpcHardwareInfo, CURVE_NONE, MIN_SUPPORTED_NUM_KEYS_IN_CSR},
};
use anyhow::Context;
use avflog::LogResult;
use binder::{
    BinderFeatures, ExceptionCode, Interface, IntoBinderResult, Result as BinderResult, Status,
    Strong,
};
use hypervisor_props::is_protected_vm_supported;
use rustutils::system_properties;
use service_vm_comm::{RequestProcessingError, Response};

/// Constructs a binder object that implements `IRemotelyProvisionedComponent`.
pub(crate) fn new_binder() -> Strong<dyn IRemotelyProvisionedComponent> {
    BnRemotelyProvisionedComponent::new_binder(
        AvfRemotelyProvisionedComponent {},
        BinderFeatures::default(),
    )
}

struct AvfRemotelyProvisionedComponent {}

impl Interface for AvfRemotelyProvisionedComponent {}

#[allow(non_snake_case)]
impl IRemotelyProvisionedComponent for AvfRemotelyProvisionedComponent {
    fn getHardwareInfo(&self) -> BinderResult<RpcHardwareInfo> {
        check_remote_attestation_is_supported()?;

        Ok(RpcHardwareInfo {
            versionNumber: 3,
            rpcAuthorName: String::from("Android Virtualization Framework"),
            supportedEekCurve: CURVE_NONE,
            uniqueId: Some(String::from("AVF Remote Provisioning 1")),
            supportedNumKeysInCsr: MIN_SUPPORTED_NUM_KEYS_IN_CSR,
        })
    }

    fn generateEcdsaP256KeyPair(
        &self,
        testMode: bool,
        macedPublicKey: &mut MacedPublicKey,
    ) -> BinderResult<Vec<u8>> {
        check_remote_attestation_is_supported()?;

        if testMode {
            return Err(Status::new_service_specific_error_str(
                STATUS_REMOVED,
                Some("generateEcdsaP256KeyPair does not support test mode in IRPC v3+ HAL."),
            ))
            .with_log();
        }
        let res = rkpvm::generate_ecdsa_p256_key_pair()
            .context("Failed to generate ECDSA P-256 key pair")
            .with_log()
            .or_service_specific_exception(STATUS_FAILED)?;
        match res {
            Response::GenerateEcdsaP256KeyPair(key_pair) => {
                macedPublicKey.macedKey = key_pair.maced_public_key;
                Ok(key_pair.key_blob)
            }
            _ => Err(to_service_specific_error(res)),
        }
        .with_log()
    }

    fn generateCertificateRequest(
        &self,
        _testMode: bool,
        _keysToSign: &[MacedPublicKey],
        _endpointEncryptionCertChain: &[u8],
        _challenge: &[u8],
        _deviceInfo: &mut DeviceInfo,
        _protectedData: &mut ProtectedData,
    ) -> BinderResult<Vec<u8>> {
        Err(Status::new_service_specific_error_str(
            STATUS_REMOVED,
            Some("This method was deprecated in v3 of the interface."),
        ))
        .with_log()
    }

    fn generateCertificateRequestV2(
        &self,
        keysToSign: &[MacedPublicKey],
        challenge: &[u8],
    ) -> BinderResult<Vec<u8>> {
        check_remote_attestation_is_supported()?;

        const MAX_CHALLENGE_SIZE: usize = 64;
        if challenge.len() > MAX_CHALLENGE_SIZE {
            let message = format!(
                "Challenge is too big. Actual: {:?}. Maximum: {:?}.",
                challenge.len(),
                MAX_CHALLENGE_SIZE
            );
            return Err(Status::new_service_specific_error_str(STATUS_FAILED, Some(message)))
                .with_log();
        }
        let res = rkpvm::generate_certificate_request(keysToSign, challenge)
            .context("Failed to generate certificate request")
            .with_log()
            .or_service_specific_exception(STATUS_FAILED)?;
        match res {
            Response::GenerateCertificateRequest(res) => Ok(res),
            _ => Err(to_service_specific_error(res)),
        }
        .with_log()
    }
}

pub(crate) fn check_remote_attestation_is_supported() -> BinderResult<()> {
    if !is_protected_vm_supported().unwrap_or(false) {
        return Err(Status::new_exception_str(
            ExceptionCode::UNSUPPORTED_OPERATION,
            Some("Protected VM support is missing for this operation"),
        ))
        .with_log();
    }
    if !is_remote_attestation_supported() {
        return Err(Status::new_exception_str(
            ExceptionCode::UNSUPPORTED_OPERATION,
            Some("Remote attestation is disabled"),
        ))
        .with_log();
    }
    Ok(())
}

pub(crate) fn is_remote_attestation_supported() -> bool {
    // Remote attestation is enabled by default.
    system_properties::read_bool("avf.remote_attestation.enabled", true).unwrap_or(true)
}

pub(crate) fn to_service_specific_error(response: Response) -> Status {
    match response {
        Response::Err(e) => match e {
            RequestProcessingError::InvalidMac => {
                Status::new_service_specific_error_str(STATUS_INVALID_MAC, Some(format!("{e}")))
            }
            _ => Status::new_service_specific_error_str(
                STATUS_FAILED,
                Some(format!("Failed to process request: {e}.")),
            ),
        },
        other => Status::new_service_specific_error_str(
            STATUS_FAILED,
            Some(format!("Incorrect response type: {other:?}")),
        ),
    }
}