#![allow(dead_code, unused_imports, unused_qualifications, unreachable_patterns)]
use super::ffi;
use crate::internal::core::{Error, Result};
const MARKER_PAYLOAD: &[u8] = b"v1";
const MARKER_ACCOUNT: &str = "__completed__";
fn service_name_for(app_name: &str) -> String {
format!("com.godaddy.{app_name}.migrate-marker")
}
#[allow(unsafe_code)] pub fn is_set(app_name: &str) -> Result<bool> {
let service = service_name_for(app_name);
let service_bytes = service.as_bytes();
let account_bytes = MARKER_ACCOUNT.as_bytes();
let service_len = i32::try_from(service_bytes.len()).map_err(|_| Error::KeyOperation {
operation: "migrate_marker_load".into(),
detail: "service name too long".into(),
})?;
let account_len = i32::try_from(account_bytes.len()).map_err(|_| Error::KeyOperation {
operation: "migrate_marker_load".into(),
detail: "account name too long".into(),
})?;
let mut out = [0_u8; 16];
let mut out_len: i32 = out.len() as i32;
let rc = unsafe {
ffi::enclaveapp_keychain_load(
service_bytes.as_ptr(),
service_len,
account_bytes.as_ptr(),
account_len,
out.as_mut_ptr(),
&mut out_len,
std::ptr::null(),
0,
0,
)
};
match rc {
0 => Ok(true),
12 => Ok(false),
_ => Err(Error::KeyOperation {
operation: "migrate_marker_load".into(),
detail: format!("Swift bridge returned error code {rc}"),
}),
}
}
#[allow(unsafe_code)] pub fn set(app_name: &str) -> Result<()> {
drop(clear(app_name));
let service = service_name_for(app_name);
let service_bytes = service.as_bytes();
let account_bytes = MARKER_ACCOUNT.as_bytes();
let service_len = i32::try_from(service_bytes.len()).map_err(|_| Error::KeyOperation {
operation: "migrate_marker_set".into(),
detail: "service name too long".into(),
})?;
let account_len = i32::try_from(account_bytes.len()).map_err(|_| Error::KeyOperation {
operation: "migrate_marker_set".into(),
detail: "account name too long".into(),
})?;
let payload_len = i32::try_from(MARKER_PAYLOAD.len()).map_err(|_| Error::KeyOperation {
operation: "migrate_marker_set".into(),
detail: "payload too long".into(),
})?;
let rc = unsafe {
ffi::enclaveapp_keychain_store(
service_bytes.as_ptr(),
service_len,
account_bytes.as_ptr(),
account_len,
MARKER_PAYLOAD.as_ptr(),
payload_len,
0, std::ptr::null(), 0,
)
};
if rc != 0 {
return Err(Error::KeyOperation {
operation: "migrate_marker_set".into(),
detail: format!("Swift bridge returned error code {rc}"),
});
}
Ok(())
}
#[allow(unsafe_code)] pub fn clear(app_name: &str) -> Result<()> {
let service = service_name_for(app_name);
let service_bytes = service.as_bytes();
let account_bytes = MARKER_ACCOUNT.as_bytes();
let service_len = i32::try_from(service_bytes.len()).map_err(|_| Error::KeyOperation {
operation: "migrate_marker_clear".into(),
detail: "service name too long".into(),
})?;
let account_len = i32::try_from(account_bytes.len()).map_err(|_| Error::KeyOperation {
operation: "migrate_marker_clear".into(),
detail: "account name too long".into(),
})?;
let rc = unsafe {
ffi::enclaveapp_keychain_delete(
service_bytes.as_ptr(),
service_len,
account_bytes.as_ptr(),
account_len,
std::ptr::null(),
0,
)
};
match rc {
0 | 12 => Ok(()),
_ => Err(Error::KeyOperation {
operation: "migrate_marker_clear".into(),
detail: format!("Swift bridge returned error code {rc}"),
}),
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::panic, let_underscore_drop)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicU64, Ordering};
static COUNTER: AtomicU64 = AtomicU64::new(0);
fn unique_app() -> String {
format!(
"enclaveapp-apple-migrate-marker-test-{}-{}",
std::process::id(),
COUNTER.fetch_add(1, Ordering::SeqCst),
)
}
#[test]
fn service_name_format() {
assert_eq!(
service_name_for("sshenc"),
"com.godaddy.sshenc.migrate-marker"
);
assert_eq!(
service_name_for("awsenc"),
"com.godaddy.awsenc.migrate-marker"
);
}
#[test]
fn service_name_for_npmenc() {
assert_eq!(
service_name_for("npmenc"),
"com.godaddy.npmenc.migrate-marker"
);
}
#[test]
fn marker_payload_is_v1() {
assert_eq!(MARKER_PAYLOAD, b"v1");
}
#[test]
fn marker_account_is_completed() {
assert_eq!(MARKER_ACCOUNT, "__completed__");
}
#[test]
fn service_name_contains_app_name_and_suffix() {
let name = service_name_for("my-app");
assert!(name.contains("my-app"));
assert!(name.ends_with(".migrate-marker"));
assert!(name.starts_with("com.godaddy."));
}
#[test]
#[ignore = "hits the real macOS Keychain; run locally"]
fn set_clear_roundtrip() {
let app = unique_app();
clear(&app).expect("clear is idempotent");
assert!(matches!(is_set(&app), Ok(false)));
set(&app).expect("set succeeds");
assert!(matches!(is_set(&app), Ok(true)));
set(&app).expect("repeat set");
assert!(matches!(is_set(&app), Ok(true)));
clear(&app).expect("clear succeeds");
assert!(matches!(is_set(&app), Ok(false)));
}
}