1use serde_with::serde_as;
2
3#[derive(serde::Serialize, serde::Deserialize, Debug)]
7#[serde(tag = "method")]
8pub enum Request {
9 #[serde(rename = "initialize")]
10 Initialize { id: u64, params: InitializeParams },
11 #[serde(rename = "sign")]
12 Sign { id: u64, params: SignParams },
13}
14
15impl Request {
16 pub fn id(&self) -> u64 {
17 match self {
18 Request::Initialize { id, .. } => *id,
19 Request::Sign { id, .. } => *id,
20 }
21 }
22}
23
24#[derive(serde::Serialize, serde::Deserialize, Debug)]
26pub struct InitializeParams {}
27
28#[serde_as]
30#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
31pub struct SignParams {
32 pub certificate_id: String,
34 pub scheme: String,
36 #[serde_as(as = "serde_with::base64::Base64")]
38 pub blob: Vec<u8>,
39}
40
41#[derive(serde::Serialize, serde::Deserialize, Debug)]
43#[serde(tag = "method")]
44pub enum SuccessResponse {
45 #[serde(rename = "initialize")]
46 Initialize { id: u64, result: InitializeResult },
47 #[serde(rename = "sign")]
48 Sign { id: u64, result: SignResult },
49}
50
51#[derive(serde::Serialize, serde::Deserialize, Debug)]
53pub struct ErrorResponse {
54 pub id: u64,
55 pub error: ErrorPayload,
56}
57
58#[derive(serde::Serialize, serde::Deserialize, Debug)]
60#[serde(untagged)]
61pub enum Response {
62 Success(SuccessResponse),
63 Error(ErrorResponse),
64}
65
66impl From<SuccessResponse> for Response {
67 fn from(s: SuccessResponse) -> Self {
68 Response::Success(s)
69 }
70}
71
72impl From<ErrorResponse> for Response {
73 fn from(e: ErrorResponse) -> Self {
74 Response::Error(e)
75 }
76}
77
78impl Response {
79 pub fn id(&self) -> u64 {
80 match self {
81 Response::Success(SuccessResponse::Initialize { id, .. }) => *id,
82 Response::Success(SuccessResponse::Sign { id, .. }) => *id,
83 Response::Error(ErrorResponse { id, .. }) => *id,
84 }
85 }
86
87 pub fn initialize(id: u64, result: Result<InitializeResult, ErrorPayload>) -> Self {
88 match result {
89 Ok(result) => SuccessResponse::Initialize { id, result }.into(),
90 Err(error) => ErrorResponse { id, error }.into(),
91 }
92 }
93
94 pub fn sign(id: u64, result: Result<SignResult, ErrorPayload>) -> Self {
95 match result {
96 Ok(result) => SuccessResponse::Sign { id, result }.into(),
97 Err(error) => ErrorResponse { id, error }.into(),
98 }
99 }
100}
101
102#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
104pub struct ErrorPayload {
105 pub code: i64,
107 pub message: String,
109}
110
111#[derive(serde::Serialize, serde::Deserialize, Debug)]
113pub struct InitializeResult {
114 pub default: String,
116 pub certificates: Vec<CertificateInfo>,
118}
119
120#[derive(serde::Serialize, serde::Deserialize, Debug)]
122pub struct CertificateInfo {
123 pub id: String,
125 pub domains: Vec<String>,
127 pub pem: String,
129 #[serde(default)]
132 pub schemes: Vec<String>,
133}
134
135#[serde_with::serde_as]
137#[derive(serde::Serialize, serde::Deserialize, Debug)]
138pub struct SignResult {
139 #[serde_as(as = "serde_with::base64::Base64")]
141 pub signature: Vec<u8>,
142}
143
144#[cfg(test)]
145mod tests {
146 #[derive(serde::Deserialize, Debug)]
147 struct WireRequest {
148 id: u64,
149 method: String,
150 #[allow(dead_code)]
151 params: serde_json::Value,
152 }
153
154 #[test]
155 fn serialize_initialize_request() {
156 let req = super::Request::Initialize {
157 id: 1,
158 params: super::InitializeParams {},
159 };
160 let json = serde_json::to_string(&req).unwrap();
161 let wire: WireRequest = serde_json::from_str(&json).unwrap();
162 assert_eq!(wire.id, 1);
163 assert_eq!(wire.method, "initialize");
164 }
165
166 #[test]
167 fn serialize_sign_request() {
168 let req = super::Request::Sign {
169 id: 42,
170 params: super::SignParams {
171 certificate_id: "cert/v1".to_owned(),
172 scheme: "ECDSA_NISTP256_SHA256".to_owned(),
173 blob: vec![0xde, 0xad, 0xbe, 0xef],
174 },
175 };
176 let json = serde_json::to_string(&req).unwrap();
177 let wire: WireRequest = serde_json::from_str(&json).unwrap();
178 assert_eq!(wire.id, 42);
179 assert_eq!(wire.method, "sign");
180 let params: super::SignParams = serde_json::from_value(wire.params).unwrap();
181 assert_eq!(params.certificate_id, "cert/v1");
182 assert_eq!(params.scheme, "ECDSA_NISTP256_SHA256");
183 assert_eq!(params.blob, vec![0xde, 0xad, 0xbe, 0xef]);
184 }
185
186 #[test]
187 fn deserialize_initialize_request() {
188 let json = r#"{"id":5,"method":"initialize","params":{}}"#;
189 let req: super::Request = serde_json::from_str(json).unwrap();
190 assert_eq!(req.id(), 5);
191 assert!(matches!(req, super::Request::Initialize { .. }));
192 }
193
194 #[test]
195 fn deserialize_sign_request() {
196 let json = r#"{"id":7,"method":"sign","params":{"certificate_id":"c1","scheme":"ED25519","blob":"AQID"}}"#;
197 let req: super::Request = serde_json::from_str(json).unwrap();
198 assert_eq!(req.id(), 7);
199 match req {
200 super::Request::Sign { params, .. } => {
201 assert_eq!(params.certificate_id, "c1");
202 assert_eq!(params.scheme, "ED25519");
203 assert_eq!(params.blob, vec![1, 2, 3]);
204 }
205 _ => panic!("expected Sign"),
206 }
207 }
208
209 #[test]
210 fn request_round_trip() {
211 let req = super::Request::Sign {
212 id: 10,
213 params: super::SignParams {
214 certificate_id: "cert/v1".to_owned(),
215 scheme: "ECDSA_NISTP256_SHA256".to_owned(),
216 blob: vec![0xde, 0xad],
217 },
218 };
219 let json = serde_json::to_string(&req).unwrap();
220 let decoded: super::Request = serde_json::from_str(&json).unwrap();
221 assert_eq!(decoded.id(), 10);
222 match decoded {
223 super::Request::Sign { params, .. } => {
224 assert_eq!(params.certificate_id, "cert/v1");
225 assert_eq!(params.blob, vec![0xde, 0xad]);
226 }
227 _ => panic!("expected Sign"),
228 }
229 }
230
231 #[test]
232 fn serialize_initialize_result_response() {
233 let resp = super::Response::Success(super::SuccessResponse::Initialize {
234 id: 1,
235 result: super::InitializeResult {
236 default: "cert1".to_owned(),
237 certificates: vec![super::CertificateInfo {
238 id: "cert1".to_owned(),
239 domains: vec!["*.example.com".to_owned()],
240 pem: "PEM DATA".to_owned(),
241 schemes: vec!["ECDSA_NISTP256_SHA256".to_owned()],
242 }],
243 },
244 });
245 let json = serde_json::to_string(&resp).unwrap();
246 assert!(json.contains("\"method\":\"initialize\""));
247 assert!(json.contains("\"result\""));
248 assert!(!json.contains("\"error\""));
249 let v: serde_json::Value = serde_json::from_str(&json).unwrap();
250 assert_eq!(v["id"], 1);
251 assert_eq!(v["method"], "initialize");
252 assert_eq!(v["result"]["default"], "cert1");
253 }
254
255 #[test]
256 fn serialize_sign_result_response() {
257 let resp = super::Response::Success(super::SuccessResponse::Sign {
258 id: 2,
259 result: super::SignResult {
260 signature: vec![0xff, 0x00, 0xab],
261 },
262 });
263 let json = serde_json::to_string(&resp).unwrap();
264 assert!(json.contains("\"method\":\"sign\""));
265 let v: serde_json::Value = serde_json::from_str(&json).unwrap();
266 assert_eq!(v["id"], 2);
267 assert_eq!(v["method"], "sign");
268 }
269
270 #[test]
271 fn serialize_error_response() {
272 let resp = super::Response::Error(super::ErrorResponse {
273 id: 3,
274 error: super::ErrorPayload {
275 code: 1,
276 message: "not found".to_owned(),
277 },
278 });
279 let json = serde_json::to_string(&resp).unwrap();
280 let v: serde_json::Value = serde_json::from_str(&json).unwrap();
281 assert_eq!(v["id"], 3);
282 assert_eq!(v["error"]["code"], 1);
283 assert_eq!(v["error"]["message"], "not found");
284 assert!(!json.contains("\"result\""));
285 assert!(!json.contains("\"method\""));
286 }
287
288 #[test]
289 fn deserialize_result_response() {
290 let json = r#"{"id":1,"method":"initialize","result":{"default":"c1","certificates":[{"id":"c1","domains":["*.test"],"pem":"---"}]}}"#;
291 let resp: super::Response = serde_json::from_str(json).unwrap();
292 assert_eq!(resp.id(), 1);
293 match resp {
294 super::Response::Success(super::SuccessResponse::Initialize { result, .. }) => {
295 assert_eq!(result.default, "c1");
296 assert_eq!(result.certificates.len(), 1);
297 }
298 _ => panic!("expected Initialize Result"),
299 }
300 }
301
302 #[test]
303 fn deserialize_error_response() {
304 let json = r#"{"id":2,"error":{"code":99,"message":"boom"}}"#;
305 let resp: super::Response = serde_json::from_str(json).unwrap();
306 assert_eq!(resp.id(), 2);
307 match resp {
308 super::Response::Error(super::ErrorResponse { error, .. }) => {
309 assert_eq!(error.code, 99);
310 assert_eq!(error.message, "boom");
311 }
312 _ => panic!("expected Error"),
313 }
314 }
315
316 #[test]
317 fn sign_params_blob_round_trip() {
318 let params = super::SignParams {
319 certificate_id: "x".to_owned(),
320 scheme: "RSA_PSS_SHA256".to_owned(),
321 blob: (0..=255).collect(),
322 };
323 let json = serde_json::to_string(¶ms).unwrap();
324 let decoded: super::SignParams = serde_json::from_str(&json).unwrap();
325 assert_eq!(decoded.blob, params.blob);
326 }
327}