swift_secure_enclave_tool_rs/
lib.rs

1use base64::engine::general_purpose::STANDARD;
2use base64::Engine;
3use rust_util::{debugging, opt_result, simple_error, XResult};
4use std::process::{Command, Output};
5
6const SWIFT_SECURE_ENCLAVE_TOOL_CMD: &str = "swift-secure-enclave-tool";
7
8#[derive(Debug, Clone, Copy)]
9pub enum KeyPurpose {
10    Signing,
11    KeyAgreement,
12}
13
14#[derive(Debug)]
15pub struct KeyMaterial {
16    pub public_key_point: Vec<u8>,
17    pub public_key_der: Vec<u8>,
18    pub private_key_representation: Vec<u8>,
19}
20
21pub fn is_secure_enclave_supported() -> XResult<bool> {
22    let mut cmd = Command::new(SWIFT_SECURE_ENCLAVE_TOOL_CMD);
23    cmd.arg("is_support_secure_enclave");
24
25    let cmd_stdout = run_command_stdout(cmd)?;
26    if cmd_stdout.starts_with("ok:") {
27        let result = cmd_stdout.chars().skip(3).collect::<String>();
28        Ok(result == "true")
29    } else {
30        simple_error!("Invalid is_secure_enclave_supported result: {}", cmd_stdout)
31    }
32}
33
34pub fn generate_ecdsa_keypair(key_purpose: KeyPurpose, require_bio: bool) -> XResult<KeyMaterial> {
35    let mut cmd = Command::new(SWIFT_SECURE_ENCLAVE_TOOL_CMD);
36    cmd.arg(match key_purpose {
37        KeyPurpose::Signing => "generate_p256_ecsign_keypair",
38        KeyPurpose::KeyAgreement => "generate_p256_ecdh_keypair",
39    });
40    cmd.arg(&format!("{}", require_bio));
41
42    let cmd_stdout = run_command_stdout(cmd)?;
43    parse_keypair_result(&cmd_stdout)
44}
45
46pub fn recover_ecdsa_keypair(
47    key_purpose: KeyPurpose,
48    private_key_representation: &[u8],
49) -> XResult<KeyMaterial> {
50    let mut cmd = Command::new(SWIFT_SECURE_ENCLAVE_TOOL_CMD);
51    cmd.arg(match key_purpose {
52        KeyPurpose::Signing => "recover_p256_ecsign_public_key",
53        KeyPurpose::KeyAgreement => "recover_p256_ecdh_public_key",
54    });
55    cmd.arg(&STANDARD.encode(private_key_representation));
56
57    let cmd_stdout = run_command_stdout(cmd)?;
58    parse_keypair_result(&cmd_stdout)
59}
60
61pub fn private_key_ecdsa_sign(
62    private_key_representation: &[u8],
63    content: &[u8],
64) -> XResult<Vec<u8>> {
65    let mut cmd = Command::new(SWIFT_SECURE_ENCLAVE_TOOL_CMD);
66    cmd.arg("compute_p256_ecsign");
67    cmd.arg(&STANDARD.encode(private_key_representation));
68    cmd.arg(&STANDARD.encode(content));
69
70    let cmd_stdout = run_command_stdout(cmd)?;
71    if cmd_stdout.starts_with("ok:") {
72        let result = cmd_stdout.chars().skip(3).collect::<String>();
73        Ok(STANDARD.decode(result)?)
74    } else {
75        simple_error!("Invalid private_key_ecdsa_sign result: {}", cmd_stdout)
76    }
77}
78
79// ephemera_public_key MUST be DER format public key
80pub fn private_key_ecdh(
81    private_key_representation: &[u8],
82    ephemera_public_key: &[u8],
83) -> XResult<Vec<u8>> {
84    let mut cmd = Command::new(SWIFT_SECURE_ENCLAVE_TOOL_CMD);
85    cmd.arg("compute_p256_ecdh");
86    cmd.arg(&STANDARD.encode(private_key_representation));
87    cmd.arg(&STANDARD.encode(ephemera_public_key));
88
89    let cmd_stdout = run_command_stdout(cmd)?;
90    if cmd_stdout.starts_with("ok:SharedSecret:") {
91        let result = cmd_stdout.chars().skip(16).collect::<String>();
92        Ok(hex::decode(result.trim())?)
93    } else {
94        simple_error!("Invalid compute_p256_ecdh result: {}", cmd_stdout)
95    }
96}
97
98fn run_command_stdout(cmd: Command) -> XResult<String> {
99    let output = run_command(cmd)?;
100    let stdout_text = opt_result!(String::from_utf8(output.stdout), "Parse stdout failed:{}");
101    Ok(stdout_text.trim().to_string())
102}
103
104fn run_command(mut cmd: Command) -> XResult<Output> {
105    debugging!("Run command: {:?}", cmd);
106    let output = cmd.output();
107    match output {
108        Err(e) => simple_error!("Run command failed: {:?}", e),
109        Ok(output) => {
110            debugging!("Output: {:?}", output);
111            if !output.status.success() {
112                simple_error!("Run command not success: {:?}", output.status.code())
113            } else {
114                Ok(output)
115            }
116        }
117    }
118}
119
120fn parse_keypair_result(cmd_stdout: &str) -> XResult<KeyMaterial> {
121    if cmd_stdout.starts_with("ok:") {
122        let result = cmd_stdout.chars().skip(3).collect::<String>();
123        let parts = result.split(",").collect::<Vec<_>>();
124        let public_key_point = STANDARD.decode(&parts[0])?;
125        let public_key_der = STANDARD.decode(&parts[1])?;
126        let private_key_representation = STANDARD.decode(&parts[2])?;
127        Ok(KeyMaterial {
128            public_key_point,
129            public_key_der,
130            private_key_representation,
131        })
132    } else {
133        simple_error!("Invalid result: {}", cmd_stdout)
134    }
135}