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, Clone, Copy, Eq, PartialEq)]
38pub enum DigestType {
39 Raw, Sha256, Sha384, Sha512,
40}
41
42impl DigestType {
43 fn to_str(&self) -> &'static str {
44 match self {
45 DigestType::Raw => "raw",
46 DigestType::Sha256 => "sha256",
47 DigestType::Sha384 => "sha384",
48 DigestType::Sha512 => "sha512",
49 }
50 }
51}
52
53#[derive(Debug)]
54pub struct KeyMaterial {
55 pub public_key_point: Vec<u8>,
56 pub public_key_der: Vec<u8>,
57 pub private_key_representation: Vec<u8>,
58}
59
60#[derive(Debug, Deserialize)]
61struct ErrorResult {
62 #[allow(dead_code)]
63 pub success: bool,
64 pub error: String,
65}
66
67#[derive(Debug, Deserialize)]
68struct IsSupportSecureEnclaveResult {
69 #[allow(dead_code)]
70 pub success: bool,
71 pub supported: bool,
72}
73
74#[derive(Debug, Deserialize)]
75struct KeyPairResult {
76 #[allow(dead_code)]
77 pub success: bool,
78 pub public_key_point_base64: String,
79 pub public_key_base64: String,
80 pub data_representation_base64: String,
81}
82
83#[derive(Debug, Deserialize)]
84struct SignResult {
85 #[allow(dead_code)]
86 pub success: bool,
87 pub signature_base64: String,
88}
89
90#[derive(Debug, Deserialize)]
91struct DhResult {
92 #[allow(dead_code)]
93 pub success: bool,
94 pub shared_secret_hex: String,
95}
96
97#[derive(Debug, Deserialize)]
98pub struct ExternalSpec {
99 #[allow(dead_code)]
100 pub success: bool,
101 pub agent: String,
102 pub specification: String,
103}
104
105#[derive(Debug, Deserialize)]
106struct ExternalPublicKey {
107 #[allow(dead_code)]
108 pub success: bool,
109 pub public_key_base64: String,
110}
111
112pub fn is_secure_enclave_supported() -> XResult<bool> {
113 let mut cmd = Command::new(SWIFT_SECURE_ENCLAVE_TOOL_CMD);
114 cmd.arg("is_support_secure_enclave");
115
116 let cmd_stdout = run_command_stdout(cmd)?;
117 if is_success(&cmd_stdout)? {
118 let is_support_se_result: IsSupportSecureEnclaveResult = from_str(&cmd_stdout)?;
119 Ok(is_support_se_result.supported)
120 } else {
121 let error_result: ErrorResult = from_str(&cmd_stdout)?;
122 simple_error!("{}", error_result.error)
123 }
124}
125
126pub fn generate_keypair(
127 key_purpose: KeyPurpose,
128 control_flag: ControlFlag,
129) -> XResult<KeyMaterial> {
130 let mut cmd = Command::new(SWIFT_SECURE_ENCLAVE_TOOL_CMD);
131 cmd.arg(match key_purpose {
132 KeyPurpose::Signing => "generate_p256_ecsign_keypair",
133 KeyPurpose::KeyAgreement => "generate_p256_ecdh_keypair",
134 });
135 cmd.arg("--control-flag");
136 cmd.arg(control_flag.to_str());
137
138 let cmd_stdout = run_command_stdout(cmd)?;
139 parse_keypair_result(&cmd_stdout)
140}
141
142pub fn recover_keypair(
143 key_purpose: KeyPurpose,
144 private_key_representation: &[u8],
145) -> XResult<KeyMaterial> {
146 let mut cmd = Command::new(SWIFT_SECURE_ENCLAVE_TOOL_CMD);
147 cmd.arg(match key_purpose {
148 KeyPurpose::Signing => "recover_p256_ecsign_public_key",
149 KeyPurpose::KeyAgreement => "recover_p256_ecdh_public_key",
150 });
151 cmd.arg("--private-key");
152 cmd.arg(STANDARD.encode(private_key_representation));
153
154 let cmd_stdout = run_command_stdout(cmd)?;
155 parse_keypair_result(&cmd_stdout)
156}
157
158pub fn private_key_sign(private_key_representation: &[u8], content: &[u8]) -> XResult<Vec<u8>> {
159 private_key_sign_digested(private_key_representation, content, DigestType::Raw)
160}
161
162pub fn private_key_sign_digested(private_key_representation: &[u8], content: &[u8], digest_type: DigestType) -> XResult<Vec<u8>> {
163 let mut cmd = Command::new(SWIFT_SECURE_ENCLAVE_TOOL_CMD);
164 cmd.arg("compute_p256_ecsign");
165 cmd.arg("--private-key");
166 cmd.arg(STANDARD.encode(private_key_representation));
167 cmd.arg("--message-base64");
168 cmd.arg(STANDARD.encode(content));
169 if digest_type != DigestType::Raw {
170 cmd.arg("--message-type");
171 cmd.arg(digest_type.to_str());
172 }
173
174 let cmd_stdout = run_command_stdout(cmd)?;
175 parse_sign_result(&cmd_stdout)
176}
177
178pub fn private_key_ecdh(
180 private_key_representation: &[u8],
181 ephemera_public_key: &[u8],
182) -> XResult<Vec<u8>> {
183 let mut cmd = Command::new(SWIFT_SECURE_ENCLAVE_TOOL_CMD);
184 cmd.arg("compute_p256_ecdh");
185 cmd.arg("--private-key");
186 cmd.arg(STANDARD.encode(private_key_representation));
187 cmd.arg("--ephemera-public-key");
188 cmd.arg(STANDARD.encode(ephemera_public_key));
189
190 let cmd_stdout = run_command_stdout(cmd)?;
191 parse_ecdh_result(&cmd_stdout)
192}
193
194pub fn external_sign(external_command: &str, parameter: &str, alg: &str, content: &[u8]) -> XResult<Vec<u8>> {
195 let mut cmd = Command::new(external_command);
196 cmd.arg("external_sign");
197 cmd.arg("--parameter");
198 cmd.arg(parameter);
199 cmd.arg("--alg");
200 cmd.arg(alg);
201 cmd.arg("--message-base64");
202 cmd.arg(STANDARD.encode(content));
203
204 let cmd_stdout = run_command_stdout(cmd)?;
205 parse_sign_result(&cmd_stdout)
206}
207
208pub fn external_ecdh(external_command: &str, parameter: &str, ephemera_public_key: &[u8]) -> XResult<Vec<u8>> {
209 let mut cmd = Command::new(external_command);
210 cmd.arg("external_ecdh");
211 cmd.arg("--parameter");
212 cmd.arg(parameter);
213 cmd.arg("--epk");
214 cmd.arg(STANDARD.encode(ephemera_public_key));
215
216 let cmd_stdout = run_command_stdout(cmd)?;
217 parse_ecdh_result(&cmd_stdout)
218}
219
220pub fn external_spec(external_command: &str) -> XResult<ExternalSpec> {
221 let mut cmd = Command::new(external_command);
222 cmd.arg("external_spec");
223
224 let cmd_stdout = run_command_stdout(cmd)?;
225 if is_success(&cmd_stdout)? {
226 let external_spec: ExternalSpec = from_str(&cmd_stdout)?;
227 Ok(external_spec)
228 } else {
229 let error_result: ErrorResult = from_str(&cmd_stdout)?;
230 simple_error!("{}", error_result.error)
231 }
232}
233
234pub fn external_public_key(external_command: &str, parameter: &str) -> XResult<Vec<u8>> {
235 let mut cmd = Command::new(external_command);
236 cmd.arg("external_public_key");
237 cmd.arg("--parameter");
238 cmd.arg(parameter);
239
240 let cmd_stdout = run_command_stdout(cmd)?;
241 if is_success(&cmd_stdout)? {
242 let external_public_key: ExternalPublicKey = from_str(&cmd_stdout)?;
243 Ok(STANDARD.decode(&external_public_key.public_key_base64)?)
244 } else {
245 let error_result: ErrorResult = from_str(&cmd_stdout)?;
246 simple_error!("{}", error_result.error)
247 }
248}
249
250fn run_command_stdout(cmd: Command) -> XResult<String> {
251 let output = run_command(cmd)?;
252 let stdout_text = opt_result!(String::from_utf8(output.stdout), "Parse stdout failed:{}");
253 Ok(stdout_text.trim().to_string())
254}
255
256fn run_command(mut cmd: Command) -> XResult<Output> {
257 debugging!("Run command: {:?}", cmd);
258 let output = cmd.output();
259 match output {
260 Err(e) => simple_error!("Run command failed: {:?}", e),
261 Ok(output) => {
262 debugging!("Output: {:?}", output);
263 if !output.status.success() {
264 let stderr = String::from_utf8_lossy(&output.stderr);
265 let stdout = String::from_utf8_lossy(&output.stdout);
266 simple_error!("Run command not success: {:?}\n - stdout: {}\n - stderr: {}", output.status.code(), stdout, stderr)
267 } else {
268 Ok(output)
269 }
270 }
271 }
272}
273
274fn parse_keypair_result(cmd_stdout: &str) -> XResult<KeyMaterial> {
275 if is_success(&cmd_stdout)? {
276 let key_pair_result: KeyPairResult = from_str(&cmd_stdout)?;
277 let public_key_point = STANDARD.decode(&key_pair_result.public_key_point_base64)?;
278 let public_key_der = STANDARD.decode(&key_pair_result.public_key_base64)?;
279 let private_key_representation =
280 STANDARD.decode(&key_pair_result.data_representation_base64)?;
281 Ok(KeyMaterial {
282 public_key_point,
283 public_key_der,
284 private_key_representation,
285 })
286 } else {
287 let error_result: ErrorResult = from_str(&cmd_stdout)?;
288 simple_error!("{}", error_result.error)
289 }
290}
291
292fn parse_sign_result(stdout: &str) -> XResult<Vec<u8>> {
293 if is_success(stdout)? {
294 let sign_result: SignResult = from_str(stdout)?;
295 Ok(STANDARD.decode(&sign_result.signature_base64)?)
296 } else {
297 let error_result: ErrorResult = from_str(stdout)?;
298 simple_error!("{}", error_result.error)
299 }
300}
301
302fn parse_ecdh_result(stdout: &str) -> XResult<Vec<u8>> {
303 if is_success(stdout)? {
304 let dh_result: DhResult = from_str(stdout)?;
305 Ok(hex::decode(&dh_result.shared_secret_hex)?)
306 } else {
307 let error_result: ErrorResult = from_str(stdout)?;
308 simple_error!("{}", error_result.error)
309 }
310}
311
312pub fn from_str<'a, T>(s: &'a str) -> XResult<T>
313where
314 T: de::Deserialize<'a>,
315{
316 match serde_json::from_str(s) {
317 Ok(result) => Ok(result),
318 Err(e) => simple_error!("Parse JSON: {}, error: {}", s, e),
319 }
320}
321
322fn is_success(cmd_stdout: &str) -> XResult<bool> {
323 let val = opt_result!(
324 serde_json::from_str::<Value>(cmd_stdout),
325 "Parse result: {}, failed: {}",
326 cmd_stdout
327 );
328 if let Value::Object(map) = val {
329 if let Some(success_value) = map.get("success") {
330 if let Value::Bool(result) = success_value {
331 return Ok(*result);
332 }
333 }
334 }
335 simple_error!("Bad result: {}", cmd_stdout)
336}