1#[cfg(doc)]
2use crate::stubs::*;
3
4use bitflags::bitflags;
5use serde::{Deserialize, Serialize};
6use serde_cbor_2::Value;
7use webauthn_rs_core::proto::{COSEEC2Key, COSEKey, COSEKeyType, COSEKeyTypeId, ECDSACurve};
8use webauthn_rs_proto::COSEAlgorithm;
9
10use self::CBORCommand;
11use super::*;
12
13#[derive(Serialize, Debug, Clone, Default, PartialEq, Eq)]
22#[serde(into = "BTreeMap<u32, Value>")]
23pub struct ClientPinRequest {
24 pub pin_uv_protocol: Option<u32>,
26 pub sub_command: ClientPinSubCommand,
28 pub key_agreement: Option<COSEKey>,
30 pub pin_uv_auth_param: Option<Vec<u8>>,
33 pub new_pin_enc: Option<Vec<u8>>,
35 pub pin_hash_enc: Option<Vec<u8>>,
37 pub permissions: Permissions,
44 pub rp_id: Option<String>,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq, Default)]
52#[repr(u32)]
53pub enum ClientPinSubCommand {
54 #[default]
55 GetPinRetries = 0x01,
56 GetKeyAgreement = 0x02,
57 SetPin = 0x03,
58 ChangePin = 0x04,
59 GetPinToken = 0x05,
60 GetPinUvAuthTokenUsingUvWithPermissions = 0x06,
61 GetUvRetries = 0x07,
62 GetPinUvAuthTokenUsingPinWithPermissions = 0x09,
63}
64
65bitflags! {
66 #[derive(Default)]
73 pub struct Permissions: u8 {
74 const MAKE_CREDENTIAL = 0x01;
75 const GET_ASSERTION = 0x02;
76 const CREDENTIAL_MANAGEMENT = 0x04;
77 const BIO_ENROLLMENT = 0x08;
78 const LARGE_BLOB_WRITE = 0x10;
79 const AUTHENTICATOR_CONFIGURATION = 0x20;
80 }
81}
82
83impl CBORCommand for ClientPinRequest {
84 const CMD: u8 = 0x06;
85 type Response = ClientPinResponse;
86}
87
88#[derive(Deserialize, Debug, Default, PartialEq, Eq)]
90#[serde(try_from = "BTreeMap<u32, Value>")]
91pub struct ClientPinResponse {
92 pub key_agreement: Option<COSEKey>,
96 pub pin_uv_auth_token: Option<Vec<u8>>,
98 pub pin_retries: Option<u32>,
100 pub power_cycle_state: Option<bool>,
105 pub uv_retries: Option<u32>,
107}
108
109impl From<ClientPinRequest> for BTreeMap<u32, Value> {
110 fn from(value: ClientPinRequest) -> Self {
111 let ClientPinRequest {
112 pin_uv_protocol,
113 sub_command,
114 key_agreement,
115 pin_uv_auth_param,
116 new_pin_enc,
117 pin_hash_enc,
118 permissions,
119 rp_id,
120 } = value;
121
122 let mut keys = BTreeMap::new();
123
124 if let Some(v) = pin_uv_protocol {
125 keys.insert(0x01, Value::Integer(v.into()));
126 }
127 keys.insert(0x02, Value::Integer((sub_command as u32).into()));
128 if let Some(v) = key_agreement {
129 if let COSEKeyType::EC_EC2(e) = v.key {
130 let m = BTreeMap::from([
132 (Value::Integer(1), Value::Integer(2)), (Value::Integer(3), Value::Integer(-25)), (Value::Integer(-1), Value::Integer(1)), (Value::Integer(-2), Value::Bytes(e.x.into())), (Value::Integer(-3), Value::Bytes(e.y.into())), ]);
138
139 keys.insert(0x03, Value::Map(m));
140 }
141 }
142 if let Some(v) = pin_uv_auth_param {
143 keys.insert(0x04, Value::Bytes(v));
144 }
145 if let Some(v) = new_pin_enc {
146 keys.insert(0x05, Value::Bytes(v));
147 }
148 if let Some(v) = pin_hash_enc {
149 keys.insert(0x06, Value::Bytes(v));
150 }
151 if !permissions.is_empty() {
152 keys.insert(0x09, Value::Integer(permissions.bits().into()));
153 }
154 if let Some(v) = rp_id {
155 keys.insert(0x0a, Value::Text(v));
156 }
157
158 keys
159 }
160}
161
162impl TryFrom<BTreeMap<u32, Value>> for ClientPinResponse {
163 type Error = &'static str;
164 fn try_from(mut raw: BTreeMap<u32, Value>) -> Result<Self, Self::Error> {
165 trace!(?raw);
166 Ok(Self {
167 key_agreement: raw
168 .remove(&0x01)
169 .and_then(|v| if let Value::Map(m) = v { Some(m) } else { None })
170 .and_then(|mut m| {
171 if m.remove(&Value::Integer(1))
180 != Some(Value::Integer(COSEKeyTypeId::EC_EC2 as i128))
181 || m.remove(&Value::Integer(3)) != Some(Value::Integer(-25))
182 || m.remove(&Value::Integer(-1))
183 != Some(Value::Integer(ECDSACurve::SECP256R1 as i128))
184 {
185 return None;
186 }
187
188 let x = m
191 .remove(&Value::Integer(-2))
192 .and_then(|v| value_to_vec_u8(v, "-2"))?;
193 let y = m
194 .remove(&Value::Integer(-3))
195 .and_then(|v| value_to_vec_u8(v, "-3"))?;
196 if x.len() != 32 || y.len() != 32 {
197 return None;
198 }
199
200 Some(COSEKey {
201 type_: COSEAlgorithm::PinUvProtocol,
202 key: COSEKeyType::EC_EC2(COSEEC2Key {
203 curve: ECDSACurve::SECP256R1,
204 x: x.to_vec().into(),
205 y: y.to_vec().into(),
206 }),
207 })
208 }),
209 pin_uv_auth_token: raw.remove(&0x02).and_then(|v| value_to_vec_u8(v, "0x02")),
210 pin_retries: raw.remove(&0x03).and_then(|v| value_to_u32(&v, "0x03")),
211 power_cycle_state: raw.remove(&0x04).and_then(|v| value_to_bool(&v, "0x04")),
212 uv_retries: raw.remove(&0x05).and_then(|v| value_to_u32(&v, "0x05")),
213 })
214 }
215}
216
217crate::deserialize_cbor!(ClientPinResponse);
218
219#[cfg(test)]
220mod tests {
221 use super::*;
222
223 #[test]
224 fn get_pin_retries() {
225 let c = ClientPinRequest {
226 pin_uv_protocol: Some(1),
227 sub_command: ClientPinSubCommand::GetPinRetries,
228 ..Default::default()
229 };
230
231 assert_eq!(
233 vec![0x06, (5 << 5) | 2, 1, 1, 2, 1],
234 c.cbor().expect("encode error")
235 );
236
237 let r = vec![0xa1, 0x03, 0x08];
238 let a = <ClientPinResponse as CBORResponse>::try_from(r.as_slice())
239 .expect("Failed to decode message");
240
241 assert_eq!(
242 ClientPinResponse {
243 pin_retries: Some(8),
244 ..Default::default()
245 },
246 a
247 );
248 }
249
250 #[test]
251 fn get_key_agreement() {
252 let c = ClientPinRequest {
253 pin_uv_protocol: Some(2),
254 sub_command: ClientPinSubCommand::GetKeyAgreement,
255 ..Default::default()
256 };
257
258 assert_eq!(
259 vec![0x06, (5 << 5) | 2, 1, 2, 2, 2],
260 c.cbor().expect("encode error")
261 );
262
263 let r = vec![
264 0xa1, 0x01, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18, 0x20, 0x01, 0x21, 0x58, 0x20, 0x74,
265 0xf4, 0x6b, 0xdc, 0x1c, 0x60, 0xac, 0xcc, 0xbb, 0xf3, 0x9a, 0x37, 0xe4, 0xcc, 0x9e,
266 0xac, 0x80, 0xf0, 0x01, 0x66, 0x27, 0xc7, 0xb6, 0x17, 0x44, 0x55, 0xb4, 0x4f, 0xe0,
267 0x4a, 0xc4, 0x70, 0x22, 0x58, 0x20, 0x38, 0xe6, 0xd6, 0xf1, 0x8d, 0xaa, 0x1f, 0x26,
268 0x9f, 0x3a, 0x95, 0xd1, 0x89, 0x34, 0xab, 0x72, 0x60, 0xe3, 0xd9, 0x50, 0x6a, 0x90,
269 0xe6, 0x8a, 0xc8, 0x35, 0xb4, 0x9f, 0xbe, 0xc4, 0x51, 0x21,
270 ];
271 let a = <ClientPinResponse as CBORResponse>::try_from(r.as_slice())
272 .expect("Failed to decode message");
273
274 assert_eq!(
275 ClientPinResponse {
276 key_agreement: Some(COSEKey {
277 type_: COSEAlgorithm::PinUvProtocol,
278 key: COSEKeyType::EC_EC2(COSEEC2Key {
279 curve: ECDSACurve::SECP256R1,
280 x: vec![
281 0x74, 0xf4, 0x6b, 0xdc, 0x1c, 0x60, 0xac, 0xcc, 0xbb, 0xf3, 0x9a, 0x37,
282 0xe4, 0xcc, 0x9e, 0xac, 0x80, 0xf0, 0x01, 0x66, 0x27, 0xc7, 0xb6, 0x17,
283 0x44, 0x55, 0xb4, 0x4f, 0xe0, 0x4a, 0xc4, 0x70
284 ]
285 .into(),
286 y: vec![
287 0x38, 0xe6, 0xd6, 0xf1, 0x8d, 0xaa, 0x1f, 0x26, 0x9f, 0x3a, 0x95, 0xd1,
288 0x89, 0x34, 0xab, 0x72, 0x60, 0xe3, 0xd9, 0x50, 0x6a, 0x90, 0xe6, 0x8a,
289 0xc8, 0x35, 0xb4, 0x9f, 0xbe, 0xc4, 0x51, 0x21
290 ]
291 .into(),
292 }),
293 }),
294 ..Default::default()
295 },
296 a,
297 );
298 }
299
300 #[test]
301 fn get_pin_token() {
302 let c = ClientPinRequest {
303 pin_uv_protocol: Some(2),
304 sub_command: ClientPinSubCommand::GetPinToken,
305 key_agreement: Some(COSEKey {
306 type_: COSEAlgorithm::PinUvProtocol,
307 key: COSEKeyType::EC_EC2(COSEEC2Key {
308 curve: ECDSACurve::SECP256R1,
309 x: vec![
310 0x76, 0xa8, 0x64, 0x59, 0xf0, 0x2a, 0xa9, 0xa7, 0x87, 0x86, 0x21, 0x26,
311 0x45, 0x6a, 0xcb, 0x6d, 0xe0, 0x7d, 0x81, 0x4b, 0x34, 0x1c, 0x10, 0xbe,
312 0xee, 0x85, 0x8c, 0x09, 0x1d, 0x0c, 0xc4, 0xde,
313 ]
314 .into(),
315 y: vec![
316 0xc9, 0xf5, 0x4d, 0x23, 0x4b, 0x33, 0x97, 0xdc, 0x86, 0xa4, 0x32, 0x44,
317 0x92, 0x0e, 0xe0, 0xfc, 0xd8, 0x81, 0x89, 0xd7, 0x58, 0xdc, 0x73, 0x92,
318 0xad, 0xf8, 0x51, 0x28, 0xdf, 0x22, 0x14, 0x25,
319 ]
320 .into(),
321 }),
322 }),
323 pin_hash_enc: Some(vec![
324 0x27, 0x5e, 0xbb, 0x6d, 0x9b, 0x29, 0xbf, 0x25, 0x77, 0xed, 0x9f, 0xfc, 0x99, 0xae,
325 0x4f, 0x29, 0xba, 0x98, 0xa4, 0x0b, 0xc7, 0x32, 0x66, 0x2a, 0xcc, 0x25, 0xa3, 0x40,
326 0x3d, 0xfa, 0x79, 0x79,
327 ]),
328 ..Default::default()
329 };
330
331 assert_eq!(
332 vec![
333 0x06, 0xa4, 0x01, 0x02, 0x02, 0x05, 0x03, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18, 0x20,
334 0x01, 0x21, 0x58, 0x20, 0x76, 0xa8, 0x64, 0x59, 0xf0, 0x2a, 0xa9, 0xa7, 0x87, 0x86,
335 0x21, 0x26, 0x45, 0x6a, 0xcb, 0x6d, 0xe0, 0x7d, 0x81, 0x4b, 0x34, 0x1c, 0x10, 0xbe,
336 0xee, 0x85, 0x8c, 0x09, 0x1d, 0x0c, 0xc4, 0xde, 0x22, 0x58, 0x20, 0xc9, 0xf5, 0x4d,
337 0x23, 0x4b, 0x33, 0x97, 0xdc, 0x86, 0xa4, 0x32, 0x44, 0x92, 0x0e, 0xe0, 0xfc, 0xd8,
338 0x81, 0x89, 0xd7, 0x58, 0xdc, 0x73, 0x92, 0xad, 0xf8, 0x51, 0x28, 0xdf, 0x22, 0x14,
339 0x25, 0x06, 0x58, 0x20, 0x27, 0x5e, 0xbb, 0x6d, 0x9b, 0x29, 0xbf, 0x25, 0x77, 0xed,
340 0x9f, 0xfc, 0x99, 0xae, 0x4f, 0x29, 0xba, 0x98, 0xa4, 0x0b, 0xc7, 0x32, 0x66, 0x2a,
341 0xcc, 0x25, 0xa3, 0x40, 0x3d, 0xfa, 0x79, 0x79
342 ],
343 c.cbor().expect("encode error")
344 );
345
346 let r = vec![
347 0xa1, 0x02, 0x58, 0x30, 0xf2, 0xbd, 0x74, 0x05, 0xff, 0x54, 0xa5, 0x2e, 0xa4, 0x49,
348 0xdd, 0x82, 0x19, 0x9e, 0x1b, 0x76, 0x25, 0x86, 0x0a, 0x5a, 0xfd, 0x09, 0x1e, 0xed,
349 0xe3, 0x0a, 0x3f, 0x76, 0x34, 0x38, 0x5e, 0xf3, 0x51, 0x40, 0x51, 0xf3, 0x91, 0xe6,
350 0x7f, 0x1a, 0x69, 0x45, 0xa9, 0x49, 0x2f, 0x1f, 0xc6, 0xc3,
351 ];
352 let a = <ClientPinResponse as CBORResponse>::try_from(r.as_slice())
353 .expect("Failed to decode message");
354
355 assert_eq!(
356 ClientPinResponse {
357 pin_uv_auth_token: Some(vec![
358 0xf2, 0xbd, 0x74, 0x05, 0xff, 0x54, 0xa5, 0x2e, 0xa4, 0x49, 0xdd, 0x82, 0x19,
359 0x9e, 0x1b, 0x76, 0x25, 0x86, 0x0a, 0x5a, 0xfd, 0x09, 0x1e, 0xed, 0xe3, 0x0a,
360 0x3f, 0x76, 0x34, 0x38, 0x5e, 0xf3, 0x51, 0x40, 0x51, 0xf3, 0x91, 0xe6, 0x7f,
361 0x1a, 0x69, 0x45, 0xa9, 0x49, 0x2f, 0x1f, 0xc6, 0xc3
362 ]),
363 ..Default::default()
364 },
365 a
366 )
367 }
368
369 #[test]
370 fn get_pin_uv_auth_token_using_uv_with_permissions() {
371 let c = ClientPinRequest {
372 pin_uv_protocol: Some(2),
373 sub_command: ClientPinSubCommand::GetPinUvAuthTokenUsingUvWithPermissions,
374 key_agreement: Some(COSEKey {
375 type_: COSEAlgorithm::PinUvProtocol,
376 key: COSEKeyType::EC_EC2(COSEEC2Key {
377 curve: ECDSACurve::SECP256R1,
378 x: vec![
379 0x18, 0x6d, 0x9d, 0x21, 0xad, 0xbe, 0x1b, 0xb0, 0x46, 0xac, 0xa9, 0x64,
380 0xdf, 0x27, 0x58, 0xd7, 0xcb, 0xdc, 0x62, 0xb0, 0x4e, 0xe6, 0x34, 0xc5,
381 0xb8, 0x12, 0xa7, 0x89, 0xd9, 0x40, 0xd9, 0xde,
382 ]
383 .into(),
384 y: vec![
385 0xfe, 0xd8, 0xf2, 0x72, 0x6f, 0x89, 0x21, 0xe2, 0xae, 0x68, 0xfe, 0x89,
386 0x66, 0x1c, 0x01, 0x6c, 0x5d, 0x0d, 0x8e, 0xd0, 0x4a, 0xe2, 0x7a, 0xd1,
387 0x1d, 0xfe, 0x49, 0xc8, 0xff, 0x7c, 0xc8, 0x7c,
388 ]
389 .into(),
390 }),
391 }),
392 permissions: Permissions::GET_ASSERTION,
393 rp_id: Some("localhost".to_string()),
394 ..Default::default()
395 };
396
397 assert_eq!(
398 vec![
399 0x06, 0xa5, 0x01, 0x02, 0x02, 0x06, 0x03, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18, 0x20,
400 0x01, 0x21, 0x58, 0x20, 0x18, 0x6d, 0x9d, 0x21, 0xad, 0xbe, 0x1b, 0xb0, 0x46, 0xac,
401 0xa9, 0x64, 0xdf, 0x27, 0x58, 0xd7, 0xcb, 0xdc, 0x62, 0xb0, 0x4e, 0xe6, 0x34, 0xc5,
402 0xb8, 0x12, 0xa7, 0x89, 0xd9, 0x40, 0xd9, 0xde, 0x22, 0x58, 0x20, 0xfe, 0xd8, 0xf2,
403 0x72, 0x6f, 0x89, 0x21, 0xe2, 0xae, 0x68, 0xfe, 0x89, 0x66, 0x1c, 0x01, 0x6c, 0x5d,
404 0x0d, 0x8e, 0xd0, 0x4a, 0xe2, 0x7a, 0xd1, 0x1d, 0xfe, 0x49, 0xc8, 0xff, 0x7c, 0xc8,
405 0x7c, 0x09, 0x02, 0x0a, 0x69, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74
406 ],
407 c.cbor().expect("encode error")
408 );
409
410 }
412
413 #[test]
414 fn set_pin() {
415 let c = ClientPinRequest {
416 pin_uv_protocol: Some(2),
417 sub_command: ClientPinSubCommand::SetPin,
418 key_agreement: Some(COSEKey {
419 type_: COSEAlgorithm::PinUvProtocol,
420 key: COSEKeyType::EC_EC2(COSEEC2Key {
421 curve: ECDSACurve::SECP256R1,
422 x: vec![
423 0x31, 0xe2, 0x7d, 0x95, 0xcd, 0x62, 0x93, 0x71, 0xf3, 0x26, 0x94, 0x8b,
424 0xbe, 0xe0, 0xd9, 0x7c, 0xdd, 0x6c, 0x39, 0xb9, 0x9e, 0x58, 0x3c, 0x40,
425 0x6d, 0x1f, 0xee, 0x60, 0xb1, 0x8f, 0xdc, 0xfd,
426 ]
427 .into(),
428 y: vec![
429 0xc0, 0xec, 0xb0, 0xae, 0x0b, 0xfc, 0xa8, 0xf6, 0x11, 0x42, 0x96, 0x4f,
430 0x56, 0xaa, 0x61, 0x7c, 0x74, 0xc5, 0x1e, 0x31, 0xf6, 0x79, 0x40, 0xd1,
431 0xb4, 0x55, 0x75, 0x20, 0xd0, 0xf1, 0xa4, 0xf7,
432 ]
433 .into(),
434 }),
435 }),
436 pin_uv_auth_param: Some(vec![
437 0x4c, 0xea, 0xbd, 0x24, 0x07, 0x61, 0xaf, 0xca, 0xf6, 0x80, 0x8c, 0x5c, 0x03, 0x93,
438 0x76, 0x3f, 0xdc, 0x90, 0x04, 0x9c, 0x1f, 0xef, 0x09, 0x18, 0x43, 0x80, 0x43, 0x5e,
439 0x18, 0xe1, 0xc0, 0x5e,
440 ]),
441 new_pin_enc: Some(vec![
442 0x66, 0x7c, 0xd5, 0xa6, 0x74, 0x8e, 0x51, 0xce, 0x8e, 0x98, 0x3a, 0x29, 0x98, 0x31,
443 0x5e, 0x1d, 0xfb, 0x33, 0x25, 0xc3, 0x36, 0xb0, 0xb5, 0xd4, 0xa7, 0xc9, 0xaa, 0x10,
444 0x28, 0x1a, 0xeb, 0xb4, 0xbf, 0x9c, 0xd4, 0x81, 0xf0, 0x67, 0x9c, 0x8b, 0x6d, 0x63,
445 0x3a, 0x76, 0xa4, 0x69, 0x07, 0x8b, 0x96, 0x92, 0xc7, 0x41, 0xac, 0xaa, 0x4e, 0xef,
446 0xc3, 0x82, 0xfe, 0x25, 0x90, 0x9a, 0x98, 0xf9, 0x2b, 0x02, 0x86, 0xfa, 0x2b, 0x53,
447 0xd6, 0x6b, 0xda, 0xa8, 0xef, 0x1d, 0x90, 0x1f, 0x9f, 0x9d,
448 ]),
449 ..Default::default()
450 };
451
452 assert_eq!(
453 vec![
454 0x06, 0xa5, 0x01, 0x02, 0x02, 0x03, 0x03, 0xa5, 0x01, 0x02, 0x03, 0x38, 0x18, 0x20,
455 0x01, 0x21, 0x58, 0x20, 0x31, 0xe2, 0x7d, 0x95, 0xcd, 0x62, 0x93, 0x71, 0xf3, 0x26,
456 0x94, 0x8b, 0xbe, 0xe0, 0xd9, 0x7c, 0xdd, 0x6c, 0x39, 0xb9, 0x9e, 0x58, 0x3c, 0x40,
457 0x6d, 0x1f, 0xee, 0x60, 0xb1, 0x8f, 0xdc, 0xfd, 0x22, 0x58, 0x20, 0xc0, 0xec, 0xb0,
458 0xae, 0x0b, 0xfc, 0xa8, 0xf6, 0x11, 0x42, 0x96, 0x4f, 0x56, 0xaa, 0x61, 0x7c, 0x74,
459 0xc5, 0x1e, 0x31, 0xf6, 0x79, 0x40, 0xd1, 0xb4, 0x55, 0x75, 0x20, 0xd0, 0xf1, 0xa4,
460 0xf7, 0x04, 0x58, 0x20, 0x4c, 0xea, 0xbd, 0x24, 0x07, 0x61, 0xaf, 0xca, 0xf6, 0x80,
461 0x8c, 0x5c, 0x03, 0x93, 0x76, 0x3f, 0xdc, 0x90, 0x04, 0x9c, 0x1f, 0xef, 0x09, 0x18,
462 0x43, 0x80, 0x43, 0x5e, 0x18, 0xe1, 0xc0, 0x5e, 0x05, 0x58, 0x50, 0x66, 0x7c, 0xd5,
463 0xa6, 0x74, 0x8e, 0x51, 0xce, 0x8e, 0x98, 0x3a, 0x29, 0x98, 0x31, 0x5e, 0x1d, 0xfb,
464 0x33, 0x25, 0xc3, 0x36, 0xb0, 0xb5, 0xd4, 0xa7, 0xc9, 0xaa, 0x10, 0x28, 0x1a, 0xeb,
465 0xb4, 0xbf, 0x9c, 0xd4, 0x81, 0xf0, 0x67, 0x9c, 0x8b, 0x6d, 0x63, 0x3a, 0x76, 0xa4,
466 0x69, 0x07, 0x8b, 0x96, 0x92, 0xc7, 0x41, 0xac, 0xaa, 0x4e, 0xef, 0xc3, 0x82, 0xfe,
467 0x25, 0x90, 0x9a, 0x98, 0xf9, 0x2b, 0x02, 0x86, 0xfa, 0x2b, 0x53, 0xd6, 0x6b, 0xda,
468 0xa8, 0xef, 0x1d, 0x90, 0x1f, 0x9f, 0x9d
469 ],
470 c.cbor().expect("encode error")
471 );
472
473 assert_eq!(
475 ClientPinResponse::default(),
476 <ClientPinResponse as CBORResponse>::try_from(&[])
477 .expect("deserialising empty response")
478 );
479 }
480}