//
// Copyright 2024 The Project Oak Authors
//
// 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.
//

//! A few bindings to SDK-provided functionality for C++ callers.
//!
//! This is not a comprehensive set of SDK functionality; it's just to bridge
//! any gaps we have between our current C++ and Rust featureset.
use std::os::raw::c_void;

use oak_crypto::encryption_key::EncryptionKey;
use oak_ffi_bytes::BytesView;
use oak_sdk_standalone::Standalone;
use prost::Message;

/// C bindings for generating standalone endorsed evidence.
/// Currently only supports the default configuration.
///
/// The provided callback will be called with the serialized EndorsedEvidence
/// proto generated by rust. Within the scope of the callback, you should
/// process the data however you'd like; but do not hold onto it, it will become
/// invalid when the callback scope is exited.
///
/// The callback also receive a caller-provided context object of the callers
/// choosing; this can contain the resources needed to properly handle the data.
///
/// This code is in extreme protoype phase, and will likely change dramatically;
/// do not depend on this for anything non-experimental
///
/// # Safety
///
///   * The semantics of `callback_context` are defined by the provided
///     callback. Ensure that the callback imlementation does not hold onto the
///     memory pointed to by `data` longer than the scope of the callback
///     invocation.
///   * `private_key` is not null.
///   * `public_key` is not null.
#[no_mangle]
pub unsafe extern "C" fn standalone_endorsed_evidence(
    callback_context: *mut c_void,
    private_key: BytesView,
    public_key: BytesView,
    callback: unsafe extern "C" fn(callback_context: *mut c_void, data: BytesView),
) -> bool {
    // Read private key from args.
    // We need to copy it because `deserialize` will zero out the bytes, but we do
    // not want to clear out the C++ caller's private key.
    // This private key isn't really needed at all; so we could refactor this later
    // to avoid passing it at all.
    let mut private_key_bytes = private_key.as_slice().to_vec();
    let private_key = EncryptionKey::deserialize(&mut private_key_bytes)
        .expect("Failed ot deserialize private key");

    // Read public key from args.
    let public_key_bytes = public_key.as_slice();

    let endorsed_evidence = Standalone::builder()
        .encryption_key_pair(Some((private_key, public_key_bytes.to_vec())))
        .build()
        .expect("failed to build standalone orchestrator")
        .endorsed_evidence();

    let serialized_endorsed_evidence = Message::encode_to_vec(&endorsed_evidence);

    let ffi_evidence = BytesView::new_from_slice(serialized_endorsed_evidence.as_slice());
    unsafe {
        callback(callback_context, ffi_evidence);
    }

    true
}
