// Copyright 2021 Contributors to the Parsec project.
// SPDX-License-Identifier: Apache-2.0
use crate::{
    handles::KeyHandle,
    structures::{Digest, HashcheckTicket, Signature, SignatureScheme, VerifiedTicket},
    tss2_esys::*,
    Context, Error, Result,
};
use log::error;
use mbox::MBox;
use std::convert::TryFrom;
use std::ptr::null_mut;

impl Context {
    /// Verify if a signature was generated by signing a given digest with a key in the TPM.
    pub fn verify_signature(
        &mut self,
        key_handle: KeyHandle,
        digest: Digest,
        signature: Signature,
    ) -> Result<VerifiedTicket> {
        let mut validation = null_mut();
        let signature = TPMT_SIGNATURE::try_from(signature)?;
        let ret = unsafe {
            Esys_VerifySignature(
                self.mut_context(),
                key_handle.into(),
                self.optional_session_1(),
                self.optional_session_2(),
                self.optional_session_3(),
                &digest.into(),
                &signature,
                &mut validation,
            )
        };
        let ret = Error::from_tss_rc(ret);

        if ret.is_success() {
            let validation = unsafe { MBox::from_raw(validation) };
            let validation = VerifiedTicket::try_from(*validation)?;
            Ok(validation)
        } else {
            error!("Error when verifying signature: {}", ret);
            Err(ret)
        }
    }

    /// Sign a digest with a key present in the TPM and return the signature.
    pub fn sign(
        &mut self,
        key_handle: KeyHandle,
        digest: Digest,
        scheme: SignatureScheme,
        validation: HashcheckTicket,
    ) -> Result<Signature> {
        let mut signature = null_mut();
        let validation = TPMT_TK_HASHCHECK::try_from(validation)?;
        let ret = unsafe {
            Esys_Sign(
                self.mut_context(),
                key_handle.into(),
                self.required_session_1()?,
                self.optional_session_2(),
                self.optional_session_3(),
                &digest.into(),
                &scheme.into(),
                &validation,
                &mut signature,
            )
        };
        let ret = Error::from_tss_rc(ret);

        if ret.is_success() {
            let signature = unsafe { MBox::from_raw(signature) };
            Ok(Signature::try_from(*signature)?)
        } else {
            error!("Error when signing: {}", ret);
            Err(ret)
        }
    }
}
