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
34#[deprecated]
35pub fn generate_ecdsa_keypair(key_purpose: KeyPurpose, require_bio: bool) -> XResult<KeyMaterial> {
36    generate_keypair(key_purpose, require_bio)
37}
38
39pub fn generate_keypair(key_purpose: KeyPurpose, require_bio: bool) -> XResult<KeyMaterial> {
40    let mut cmd = Command::new(SWIFT_SECURE_ENCLAVE_TOOL_CMD);
41    cmd.arg(match key_purpose {
42        KeyPurpose::Signing => "generate_p256_ecsign_keypair",
43        KeyPurpose::KeyAgreement => "generate_p256_ecdh_keypair",
44    });
45    cmd.arg(format!("{}", require_bio));
46
47    let cmd_stdout = run_command_stdout(cmd)?;
48    parse_keypair_result(&cmd_stdout)
49}
50
51#[deprecated]
52pub fn recover_ecdsa_keypair(
53    key_purpose: KeyPurpose,
54    private_key_representation: &[u8],
55) -> XResult<KeyMaterial> {
56    recover_keypair(key_purpose, private_key_representation)
57}
58
59pub fn recover_keypair(
60    key_purpose: KeyPurpose,
61    private_key_representation: &[u8],
62) -> XResult<KeyMaterial> {
63    let mut cmd = Command::new(SWIFT_SECURE_ENCLAVE_TOOL_CMD);
64    cmd.arg(match key_purpose {
65        KeyPurpose::Signing => "recover_p256_ecsign_public_key",
66        KeyPurpose::KeyAgreement => "recover_p256_ecdh_public_key",
67    });
68    cmd.arg(STANDARD.encode(private_key_representation));
69
70    let cmd_stdout = run_command_stdout(cmd)?;
71    parse_keypair_result(&cmd_stdout)
72}
73
74#[deprecated]
75pub fn private_key_ecdsa_sign(
76    private_key_representation: &[u8],
77    content: &[u8],
78) -> XResult<Vec<u8>> {
79    private_key_sign(private_key_representation, content)
80}
81
82pub fn private_key_sign(private_key_representation: &[u8], content: &[u8]) -> XResult<Vec<u8>> {
83    let mut cmd = Command::new(SWIFT_SECURE_ENCLAVE_TOOL_CMD);
84    cmd.arg("compute_p256_ecsign");
85    cmd.arg(STANDARD.encode(private_key_representation));
86    cmd.arg(STANDARD.encode(content));
87
88    let cmd_stdout = run_command_stdout(cmd)?;
89    if cmd_stdout.starts_with("ok:") {
90        let result = cmd_stdout.chars().skip(3).collect::<String>();
91        Ok(STANDARD.decode(result)?)
92    } else {
93        simple_error!("Invalid private_key_ecdsa_sign result: {}", cmd_stdout)
94    }
95}
96
97// ephemera_public_key MUST be DER format public key
98pub fn private_key_ecdh(
99    private_key_representation: &[u8],
100    ephemera_public_key: &[u8],
101) -> XResult<Vec<u8>> {
102    let mut cmd = Command::new(SWIFT_SECURE_ENCLAVE_TOOL_CMD);
103    cmd.arg("compute_p256_ecdh");
104    cmd.arg(STANDARD.encode(private_key_representation));
105    cmd.arg(STANDARD.encode(ephemera_public_key));
106
107    let cmd_stdout = run_command_stdout(cmd)?;
108    if cmd_stdout.starts_with("ok:SharedSecret:") {
109        let result = cmd_stdout.chars().skip(16).collect::<String>();
110        Ok(hex::decode(result.trim())?)
111    } else {
112        simple_error!("Invalid compute_p256_ecdh result: {}", cmd_stdout)
113    }
114}
115
116fn run_command_stdout(cmd: Command) -> XResult<String> {
117    let output = run_command(cmd)?;
118    let stdout_text = opt_result!(String::from_utf8(output.stdout), "Parse stdout failed:{}");
119    Ok(stdout_text.trim().to_string())
120}
121
122fn run_command(mut cmd: Command) -> XResult<Output> {
123    debugging!("Run command: {:?}", cmd);
124    let output = cmd.output();
125    match output {
126        Err(e) => simple_error!("Run command failed: {:?}", e),
127        Ok(output) => {
128            debugging!("Output: {:?}", output);
129            if !output.status.success() {
130                simple_error!("Run command not success: {:?}", output.status.code())
131            } else {
132                Ok(output)
133            }
134        }
135    }
136}
137
138fn parse_keypair_result(cmd_stdout: &str) -> XResult<KeyMaterial> {
139    if cmd_stdout.starts_with("ok:") {
140        let result = cmd_stdout.chars().skip(3).collect::<String>();
141        let parts = result.split(",").collect::<Vec<_>>();
142        let public_key_point = STANDARD.decode(parts[0])?;
143        let public_key_der = STANDARD.decode(parts[1])?;
144        let private_key_representation = STANDARD.decode(parts[2])?;
145        Ok(KeyMaterial {
146            public_key_point,
147            public_key_der,
148            private_key_representation,
149        })
150    } else {
151        simple_error!("Invalid result: {}", cmd_stdout)
152    }
153}