keycache 0.1.2

Seals a key using a secure element
//! Platform implementation for macOS which uses the secure enclave

use crate::keywrap::AuthLevel;
use keycache_libsecureenclave_sys::{LibSecureEnclave, sep_buf_t, sep_permissions_t};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::fmt::{Display, Formatter};
use std::io::{Error, ErrorKind};

// Derive serde for `sep_buf_t`
#[derive(Serialize, Deserialize)]
#[serde(remote = "sep_buf_t")]
#[allow(unused, reason = "serde reference to autogenerated binding")]
#[allow(non_camel_case_types, reason = "reference to autogenerated binding")]
struct sep_buf_t__serdedef {
    len: usize,
    #[serde(with = "serde_bytes")]
    bytes: [u8; 2040],
}

/// Secure enclave error type
#[derive(Debug, Clone, Copy)]
struct SecureEnclaveError(sep_buf_t);
impl Display for SecureEnclaveError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match str::from_utf8(&self.0.bytes[..self.0.len]) {
            Ok(msg) => write!(f, "{msg}"),
            Err(_) => write!(f, "<non-utf8 error message>"),
        }
    }
}
impl std::error::Error for SecureEnclaveError {
    // No members to implement
}

/// The metadata necessary to reimport and use the private key
#[derive(Debug, Clone, Serialize, Deserialize)]
#[repr(C)]
struct Metadata {
    /// Secure-enclave exported private key blob
    ///
    /// ## Note
    /// This blob contains the private key in an opaque, sealed form. It can only be imported and used by the specific
    /// secure enclave that created it. Outside that secure enclave, it is fully encrypted and completely unusable.
    #[serde(with = "sep_buf_t__serdedef")]
    private: sep_buf_t,
    /// The ephemeral public key half for the ECDH key exchange
    #[serde(with = "sep_buf_t__serdedef")]
    ephemeral: sep_buf_t,
}

/// Creates a new hardware-backed key-encryption-key and returns the necessary metadata to use the key later
pub fn create(auth_level: AuthLevel) -> Result<Vec<u8>, Error> {
    /// Secure enclave permissions for ephemeral keys
    const EPHEMERAL_PERMS: sep_permissions_t = sep_permissions_t::sep_permissions_needs_unlock_once;

    // Load library and allocate error buffer
    let libsecureenclave = unsafe { LibSecureEnclave::load() };
    let mut error = SecureEnclaveError(sep_buf_t { len: 0, bytes: [0; _] });

    // Map authentication level into secure enclave permissions
    let permissions = match auth_level {
        AuthLevel::Unauthenticated => sep_permissions_t::sep_permissions_needs_unlock_once,
        AuthLevel::Interactive => sep_permissions_t::sep_permissions_needs_interactive_auth,
        AuthLevel::Biometry => sep_permissions_t::sep_permissions_needs_same_biometry,
    };

    // Generate first keypair
    let mut private = sep_buf_t { len: 0, bytes: [0; _] };
    let retval = unsafe { (libsecureenclave.sep_p256_generate)(permissions, &mut private, &mut error.0) };
    assert_eq!(retval, 0, "failed to generate sep private key");

    // Generate ephemeral private key
    let mut ephemeral_private = sep_buf_t { len: 0, bytes: [0; _] };
    let retval = unsafe { (libsecureenclave.sep_p256_generate)(EPHEMERAL_PERMS, &mut ephemeral_private, &mut error.0) };
    assert_eq!(retval, 0, "failed to generate sep private key");

    // Get associated public key
    let mut ephemeral = sep_buf_t { len: 0, bytes: [0; _] };
    let retval = unsafe { (libsecureenclave.sep_p256_publickey)(&ephemeral_private, &mut ephemeral, &mut error.0) };
    assert_eq!(retval, 0, "failed to get public key for sep private key");

    // Convert blob to Rust-vec
    let metadata = Metadata { private, ephemeral };
    let metadata = serde_asn1_der::to_vec(&metadata).expect("failed to serialize metadata");
    Ok(metadata)
}

/// Unlocks a key-encryption-key via the associated metadata
pub fn unlock(metadata: &[u8], _user_auth: Option<&[u8]>) -> Result<[u8; 32], Error> {
    // Load library and allocate error buffer
    let libsecureenclave = unsafe { LibSecureEnclave::load() };
    let mut error = SecureEnclaveError(sep_buf_t { len: 0, bytes: [0; _] });

    // Parse metadata
    let Metadata { private, ephemeral } =
        serde_asn1_der::from_bytes(metadata).map_err(|e| Error::new(ErrorKind::InvalidData, e))?;

    // Generate shared secret
    let mut shared_secret = sep_buf_t { len: 0, bytes: [0; _] };
    let retval =
        unsafe { (libsecureenclave.sep_p256_keyexchange)(&private, &ephemeral, &mut shared_secret, &mut error.0) };
    let 0 = retval else {
        // The secure enclave refused cooperation
        return Err(Error::new(ErrorKind::PermissionDenied, error));
    };

    // Digest shared secret
    let shared_secret = Sha256::digest(&shared_secret.bytes[..shared_secret.len]);
    Ok(shared_secret.into())
}