1#![allow(clippy::never_loop)]
2#![allow(clippy::result_large_err)]
3
4pub mod error;
5
6use error::EIMZOError;
7use native_tls::{TlsConnector, TlsStream};
8use serde::{Deserialize, Serialize};
9use serde_json::json;
10use std::{collections::HashMap, net::TcpStream};
11use tungstenite::{
12 Message, WebSocket,
13 client::client,
14 handshake::client::{Request, generate_key},
15};
16use url::Url;
17
18#[derive(Serialize, Deserialize, Debug, Clone)]
19pub struct Certificate {
20 pub disk: String,
21 pub path: String,
22 pub name: String,
23 pub alias: String,
24}
25
26impl Certificate {
27 pub fn get_alias(&self) -> HashMap<String, String> {
28 self.alias
29 .split(",")
30 .filter_map(|kv| {
31 let mut kv = kv.split("=");
32 match (kv.next(), kv.next()) {
33 (Some(k), Some(v)) => Some((k.to_string(), v.to_string())),
34 _ => None,
35 }
36 })
37 .collect()
38 }
39}
40
41pub struct EIMZOConnection {
42 pub socket: WebSocket<TlsStream<TcpStream>>,
43}
44
45impl EIMZOConnection {
46 fn connect() -> Result<Self, EIMZOError> {
47 let ws_url = Url::parse("wss://127.0.0.1:64443/service/cryptapi")?;
48
49 let tls_connector = TlsConnector::builder()
51 .danger_accept_invalid_certs(true)
52 .build()?;
53
54 let remote_addr = match (ws_url.host(), ws_url.port()) {
55 (Some(host), Some(port)) => Some(format!("{host}:{port}")),
56 _ => None,
57 }
58 .ok_or(())
59 .unwrap();
60
61 let req = Request::builder()
62 .method("GET")
63 .header("Host", "localhost")
64 .header("Connection", "Upgrade")
65 .header("Upgrade", "websocket")
66 .header("Origin", "https://localhost")
67 .header("Sec-WebSocket-Version", "13")
68 .header("Sec-WebSocket-Key", generate_key())
69 .uri(ws_url.to_string())
70 .body(())
71 .unwrap();
72 let tcp_stream = std::net::TcpStream::connect(remote_addr.clone())?;
73 let tls_stream = tls_connector.connect(remote_addr.as_str(), tcp_stream)?;
74
75 let (socket, _) = client(req, tls_stream)?;
76
77 let connection = Self { socket };
78
79 Ok(connection)
80 }
81
82 pub fn send_and_wait(&mut self, message: Message) -> tungstenite::Result<Message> {
83 self.socket.send(message)?;
84
85 while let Ok(message) = self.socket.read() {
86 return Ok(message);
87 }
88
89 unreachable!();
90 }
91
92 pub fn set_api_keys(&mut self) -> tungstenite::Result<Message> {
93 let set_api_keys = json!({
94 "plugin": "apikey",
95 "name": "apikey",
96 "arguments": [
97 "localhost",
98 "96D0C1491615C82B9A54D9989779DF825B690748224C2B04F500F370D51827CE2644D8D4A82C18184D73AB8530BB8ED537269603F61DB0D03D2104ABF789970B",
99 ]
100 });
101
102 self.send_and_wait(Message::Text(set_api_keys.to_string().into()))
103 }
104}
105
106#[derive(Serialize, Deserialize, Default, Debug)]
107pub struct ListAllCertificatesResponse {
108 pub certificates: Vec<Certificate>,
109}
110
111pub fn list_all_certificates() -> Result<Vec<Certificate>, EIMZOError> {
112 let mut conn: EIMZOConnection = EIMZOConnection::connect()?;
113
114 let _ = conn.set_api_keys();
115
116 let cmd: serde_json::Value = json!({
117 "plugin": "pfx",
118 "name": "list_all_certificates",
119 });
120
121 let value = match conn.send_and_wait(Message::Text(cmd.to_string().into())) {
122 Ok(Message::Text(str)) => serde_json::from_str::<ListAllCertificatesResponse>(&str),
123 _ => Ok(ListAllCertificatesResponse::default()),
124 };
125
126 Ok(value.map(|s| s.certificates)?)
127}