// 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::{Esys_Sign, Esys_VerifySignature},
    Context, Error, Result,
};
use log::error;
use std::convert::{TryFrom, TryInto};
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_ptr = null_mut();
        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.try_into()?,
                &mut validation_ptr,
            )
        };
        let ret = Error::from_tss_rc(ret);

        if ret.is_success() {
            VerifiedTicket::try_from(Context::ffi_data_to_owned(validation_ptr))
        } 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_ptr = null_mut();
        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.try_into()?,
                &mut signature_ptr,
            )
        };
        let ret = Error::from_tss_rc(ret);

        if ret.is_success() {
            Signature::try_from(Context::ffi_data_to_owned(signature_ptr))
        } else {
            error!("Error when signing: {}", ret);
            Err(ret)
        }
    }
}
