swift_secure_enclave_tool_rs/
lib.rs1use base64::Engine;
2use base64::engine::general_purpose::STANDARD;
3use rust_util::{XResult, debugging, opt_result, simple_error};
4use serde::{Deserialize, de};
5use serde_json::Value;
6use std::process::{Command, Output};
7
8const SWIFT_SECURE_ENCLAVE_TOOL_CMD: &str = "swift-secure-enclave-tool-v2";
9
10#[derive(Debug, Clone, Copy)]
11pub enum KeyPurpose {
12 Signing,
13 KeyAgreement,
14}
15
16#[derive(Debug, Clone, Copy)]
17pub enum ControlFlag {
18 None,
19 UserPresence,
20 DevicePasscode,
21 BiometryAny,
22 BiometryCurrentSet,
23}
24
25impl ControlFlag {
26 fn to_str(&self) -> &'static str {
27 match self {
28 ControlFlag::None => "none",
29 ControlFlag::UserPresence => "userPresence",
30 ControlFlag::DevicePasscode => "devicePasscode",
31 ControlFlag::BiometryAny => "biometryAny",
32 ControlFlag::BiometryCurrentSet => "biometryCurrentSet",
33 }
34 }
35}
36
37#[derive(Debug)]
38pub struct KeyMaterial {
39 pub public_key_point: Vec<u8>,
40 pub public_key_der: Vec<u8>,
41 pub private_key_representation: Vec<u8>,
42}
43
44#[derive(Debug, Deserialize)]
45struct ErrorResult {
46 #[allow(dead_code)]
47 pub success: bool,
48 pub error: String,
49}
50
51#[derive(Debug, Deserialize)]
52struct IsSupportSecureEnclaveResult {
53 #[allow(dead_code)]
54 pub success: bool,
55 pub supported: bool,
56}
57
58#[derive(Debug, Deserialize)]
59struct KeyPairResult {
60 #[allow(dead_code)]
61 pub success: bool,
62 pub public_key_point_base64: String,
63 pub public_key_base64: String,
64 pub data_representation_base64: String,
65}
66
67#[derive(Debug, Deserialize)]
68struct SignResult {
69 #[allow(dead_code)]
70 pub success: bool,
71 pub signature_base64: String,
72}
73
74#[derive(Debug, Deserialize)]
75struct DhResult {
76 #[allow(dead_code)]
77 pub success: bool,
78 pub shared_secret_hex: String,
79}
80
81pub fn is_secure_enclave_supported() -> XResult<bool> {
82 let mut cmd = Command::new(SWIFT_SECURE_ENCLAVE_TOOL_CMD);
83 cmd.arg("is_support_secure_enclave");
84
85 let cmd_stdout = run_command_stdout(cmd)?;
86 if is_success(&cmd_stdout)? {
87 let is_support_se_result: IsSupportSecureEnclaveResult = from_str(&cmd_stdout)?;
88 Ok(is_support_se_result.supported)
89 } else {
90 let error_result: ErrorResult = from_str(&cmd_stdout)?;
91 simple_error!("{}", error_result.error)
92 }
93}
94
95pub fn generate_keypair(
96 key_purpose: KeyPurpose,
97 control_flag: ControlFlag,
98) -> XResult<KeyMaterial> {
99 let mut cmd = Command::new(SWIFT_SECURE_ENCLAVE_TOOL_CMD);
100 cmd.arg(match key_purpose {
101 KeyPurpose::Signing => "generate_p256_ecsign_keypair",
102 KeyPurpose::KeyAgreement => "generate_p256_ecdh_keypair",
103 });
104 cmd.arg("--control-flag");
105 cmd.arg(control_flag.to_str());
106
107 let cmd_stdout = run_command_stdout(cmd)?;
108 parse_keypair_result(&cmd_stdout)
109}
110
111pub fn recover_keypair(
112 key_purpose: KeyPurpose,
113 private_key_representation: &[u8],
114) -> XResult<KeyMaterial> {
115 let mut cmd = Command::new(SWIFT_SECURE_ENCLAVE_TOOL_CMD);
116 cmd.arg(match key_purpose {
117 KeyPurpose::Signing => "recover_p256_ecsign_public_key",
118 KeyPurpose::KeyAgreement => "recover_p256_ecdh_public_key",
119 });
120 cmd.arg("--private-key");
121 cmd.arg(STANDARD.encode(private_key_representation));
122
123 let cmd_stdout = run_command_stdout(cmd)?;
124 parse_keypair_result(&cmd_stdout)
125}
126
127pub fn private_key_sign(private_key_representation: &[u8], content: &[u8]) -> XResult<Vec<u8>> {
128 let mut cmd = Command::new(SWIFT_SECURE_ENCLAVE_TOOL_CMD);
129 cmd.arg("compute_p256_ecsign");
130 cmd.arg("--private-key");
131 cmd.arg(STANDARD.encode(private_key_representation));
132 cmd.arg("--message-base64");
133 cmd.arg(STANDARD.encode(content));
134
135 let cmd_stdout = run_command_stdout(cmd)?;
136
137 if is_success(&cmd_stdout)? {
138 let sign_result: SignResult = from_str(&cmd_stdout)?;
139 Ok(STANDARD.decode(&sign_result.signature_base64)?)
140 } else {
141 let error_result: ErrorResult = from_str(&cmd_stdout)?;
142 simple_error!("{}", error_result.error)
143 }
144}
145
146pub fn private_key_ecdh(
148 private_key_representation: &[u8],
149 ephemera_public_key: &[u8],
150) -> XResult<Vec<u8>> {
151 let mut cmd = Command::new(SWIFT_SECURE_ENCLAVE_TOOL_CMD);
152 cmd.arg("compute_p256_ecdh");
153 cmd.arg("--private-key");
154 cmd.arg(STANDARD.encode(private_key_representation));
155 cmd.arg("--ephemera-public-key");
156 cmd.arg(STANDARD.encode(ephemera_public_key));
157
158 let cmd_stdout = run_command_stdout(cmd)?;
159
160 if is_success(&cmd_stdout)? {
161 let dh_result: DhResult = from_str(&cmd_stdout)?;
162 Ok(hex::decode(&dh_result.shared_secret_hex)?)
163 } else {
164 let error_result: ErrorResult = from_str(&cmd_stdout)?;
165 simple_error!("{}", error_result.error)
166 }
167}
168
169fn run_command_stdout(cmd: Command) -> XResult<String> {
170 let output = run_command(cmd)?;
171 let stdout_text = opt_result!(String::from_utf8(output.stdout), "Parse stdout failed:{}");
172 Ok(stdout_text.trim().to_string())
173}
174
175fn run_command(mut cmd: Command) -> XResult<Output> {
176 debugging!("Run command: {:?}", cmd);
177 let output = cmd.output();
178 match output {
179 Err(e) => simple_error!("Run command failed: {:?}", e),
180 Ok(output) => {
181 debugging!("Output: {:?}", output);
182 if !output.status.success() {
183 simple_error!("Run command not success: {:?}", output.status.code())
184 } else {
185 Ok(output)
186 }
187 }
188 }
189}
190
191fn parse_keypair_result(cmd_stdout: &str) -> XResult<KeyMaterial> {
192 if is_success(&cmd_stdout)? {
193 let key_pair_result: KeyPairResult = from_str(&cmd_stdout)?;
194 let public_key_point = STANDARD.decode(&key_pair_result.public_key_point_base64)?;
195 let public_key_der = STANDARD.decode(&key_pair_result.public_key_base64)?;
196 let private_key_representation =
197 STANDARD.decode(&key_pair_result.data_representation_base64)?;
198 Ok(KeyMaterial {
199 public_key_point,
200 public_key_der,
201 private_key_representation,
202 })
203 } else {
204 let error_result: ErrorResult = from_str(&cmd_stdout)?;
205 simple_error!("{}", error_result.error)
206 }
207}
208
209pub fn from_str<'a, T>(s: &'a str) -> XResult<T>
210where
211 T: de::Deserialize<'a>,
212{
213 match serde_json::from_str(s) {
214 Ok(result) => Ok(result),
215 Err(e) => simple_error!("Parse JSON: {}, error: {}", s, e),
216 }
217}
218
219fn is_success(cmd_stdout: &str) -> XResult<bool> {
220 let val = opt_result!(
221 serde_json::from_str::<Value>(cmd_stdout),
222 "Parse result: {}, failed: {}",
223 cmd_stdout
224 );
225 if let Value::Object(map) = val {
226 if let Some(success_value) = map.get("success") {
227 if let Value::Bool(result) = success_value {
228 return Ok(*result);
229 }
230 }
231 }
232 simple_error!("Bad result: {}", cmd_stdout)
233}