1use crate::http_uri_ext::HttpUriExt as _;
10use http::{Response, Uri};
11use hyper::header::ETAG;
12use p256::ecdsa::{signature::Verifier as _, DerSignature};
13use rand::{thread_rng, Rng};
14use serde::{Deserialize, Deserializer, Serialize, Serializer};
15use sha2::{digest, Digest, Sha256};
16use signature::Signature;
17use std::{collections::HashMap, convert::TryInto, fmt, fmt::Debug};
18
19#[derive(Debug, thiserror::Error)]
21pub enum CupDecorationError {
22 #[error("could not serialize request.")]
23 SerializationError(#[from] serde_json::Error),
24 #[error("could not parse existing URI.")]
25 ParseError(#[from] http::uri::InvalidUri),
26 #[error("could not append query parameter.")]
27 AppendQueryParameterError(#[from] crate::http_uri_ext::Error),
28}
29
30#[derive(Debug, thiserror::Error)]
32pub enum CupVerificationError {
33 #[error("etag header missing.")]
34 EtagHeaderMissing,
35 #[error("etag header is not a string.")]
36 EtagNotString(hyper::header::ToStrError),
37 #[error("etag header is malformed.")]
38 EtagMalformed,
39 #[error("etag header's request hash is malformed.")]
40 RequestHashMalformed,
41 #[error("etag header's request hash doesn't match.")]
42 RequestHashMismatch,
43 #[error("etag header's signature is malformed.")]
44 SignatureMalformed,
45 #[error("specified public key ID not found in internal map.")]
46 SpecifiedPublicKeyIdMissing,
47 #[error("could not verify etag header's signature.")]
48 SignatureError(#[from] ecdsa::Error),
49}
50
51pub type PublicKeyId = u64;
54pub type PublicKey = p256::ecdsa::VerifyingKey;
55
56fn from_pem<'de, D>(deserializer: D) -> Result<PublicKey, D::Error>
57where
58 D: Deserializer<'de>,
59{
60 use serde::de;
61 let s = String::deserialize(deserializer)?;
62 s.parse().map_err(de::Error::custom)
63}
64
65fn to_pem<S>(public_key: &PublicKey, serializer: S) -> Result<S::Ok, S::Error>
66where
67 S: Serializer,
68{
69 use pkcs8::EncodePublicKey;
70 use serde::ser;
71 serializer.serialize_str(
72 &elliptic_curve::PublicKey::from(public_key)
73 .to_public_key_pem(pkcs8::LineEnding::LF)
74 .map_err(ser::Error::custom)?,
75 )
76}
77
78#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
79pub struct PublicKeyAndId {
80 #[serde(deserialize_with = "from_pem", serialize_with = "to_pem")]
81 pub key: PublicKey,
82 pub id: PublicKeyId,
83}
84
85#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
86pub struct PublicKeys {
87 pub latest: PublicKeyAndId,
89 pub historical: Vec<PublicKeyAndId>,
91}
92
93#[derive(PartialEq, Eq, Debug, Copy, Clone)]
94pub struct Nonce([u8; 32]);
95
96impl From<[u8; 32]> for Nonce {
97 fn from(array: [u8; 32]) -> Self {
98 Nonce(array)
99 }
100}
101impl From<&[u8; 32]> for Nonce {
102 fn from(array: &[u8; 32]) -> Self {
103 Nonce(*array)
104 }
105}
106
107#[allow(clippy::from_over_into)]
108impl Into<[u8; 32]> for Nonce {
109 fn into(self) -> [u8; 32] {
110 self.0
111 }
112}
113
114impl Default for Nonce {
115 fn default() -> Self {
116 Self::new()
117 }
118}
119
120impl Nonce {
121 pub fn new() -> Nonce {
122 let mut nonce_bits = [0_u8; 32];
123 thread_rng().fill(&mut nonce_bits[..]);
124 Nonce(nonce_bits)
125 }
126}
127
128impl fmt::Display for Nonce {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 write!(f, "{}", hex::encode(self.0))
131 }
132}
133
134#[derive(Clone, Debug, PartialEq, Eq)]
138pub struct RequestMetadata {
139 pub request_body: Vec<u8>,
140 pub public_key_id: PublicKeyId,
141 pub nonce: Nonce,
142}
143
144pub trait CupRequest {
147 fn get_uri(&self) -> &str;
149 fn set_uri(&mut self, uri: String);
151 fn get_serialized_body(&self) -> serde_json::Result<Vec<u8>>;
153}
154
155pub trait Cupv2RequestHandler {
158 fn decorate_request(
162 &self,
163 request: &mut impl CupRequest,
164 ) -> Result<RequestMetadata, CupDecorationError>;
165
166 fn verify_response(
169 &self,
170 request_metadata: &RequestMetadata,
171 resp: &Response<Vec<u8>>,
172 public_key_id: PublicKeyId,
173 ) -> Result<DerSignature, CupVerificationError>;
174}
175
176pub trait Cupv2Verifier {
178 fn verify_response_with_signature(
181 &self,
182 ecdsa_signature: &DerSignature,
183 request_body: &[u8],
184 response_body: &[u8],
185 public_key_id: PublicKeyId,
186 nonce: &Nonce,
187 ) -> Result<(), CupVerificationError>;
188}
189
190pub trait Cupv2Handler: Cupv2RequestHandler + Cupv2Verifier {}
191
192impl<T> Cupv2Handler for T where T: Cupv2RequestHandler + Cupv2Verifier {}
193
194#[derive(Debug)]
196pub struct StandardCupv2Handler {
197 parameters_by_id: HashMap<PublicKeyId, PublicKey>,
200 latest_public_key_id: PublicKeyId,
201}
202
203impl StandardCupv2Handler {
204 pub fn new(public_keys: &PublicKeys) -> Self {
206 Self {
207 parameters_by_id: std::iter::once(&public_keys.latest)
208 .chain(&public_keys.historical)
209 .map(|k| (k.id, k.key))
210 .collect(),
211 latest_public_key_id: public_keys.latest.id,
212 }
213 }
214}
215
216impl Cupv2RequestHandler for StandardCupv2Handler {
217 fn decorate_request(
218 &self,
219 request: &mut impl CupRequest,
220 ) -> Result<RequestMetadata, CupDecorationError> {
221 let public_key_id: PublicKeyId = self.latest_public_key_id;
229
230 let nonce = Nonce::new();
231
232 let uri: Uri = request.get_uri().parse()?;
233 let uri = uri.append_query_parameter("cup2key", &format!("{public_key_id}:{nonce}"))?;
234 request.set_uri(uri.to_string());
235
236 Ok(RequestMetadata {
237 request_body: request.get_serialized_body()?,
238 public_key_id,
239 nonce,
240 })
241 }
242
243 fn verify_response(
244 &self,
245 request_metadata: &RequestMetadata,
246 resp: &Response<Vec<u8>>,
247 public_key_id: PublicKeyId,
248 ) -> Result<DerSignature, CupVerificationError> {
249 let etag_header = resp
265 .headers()
266 .get(ETAG)
267 .ok_or(CupVerificationError::EtagHeaderMissing)?
268 .to_str()
269 .map_err(CupVerificationError::EtagNotString)
270 .map(parse_etag)?;
271
272 let (encoded_signature, hex_hash): (&str, &str) = etag_header
273 .split_once(':')
274 .ok_or(CupVerificationError::EtagMalformed)?;
275
276 let actual_hash =
277 &hex::decode(hex_hash).map_err(|_| CupVerificationError::RequestHashMalformed)?;
278
279 let request_body_hash = Sha256::digest(&request_metadata.request_body);
280 if *request_body_hash != *actual_hash {
281 return Err(CupVerificationError::RequestHashMismatch);
282 }
283
284 let signature = DerSignature::from_bytes(
285 &hex::decode(encoded_signature)
286 .map_err(|_| CupVerificationError::SignatureMalformed)?,
287 )?;
288
289 let () = self.verify_response_with_signature(
290 &signature,
291 &request_metadata.request_body,
292 resp.body(),
293 public_key_id,
294 &request_metadata.nonce,
295 )?;
296
297 Ok(signature)
298 }
299}
300
301pub fn make_transaction_hash(
302 request_body: &[u8],
303 response_body: &[u8],
304 public_key_id: PublicKeyId,
305 nonce: &Nonce,
306) -> digest::Output<Sha256> {
307 let request_hash = Sha256::digest(request_body);
308 let response_hash = Sha256::digest(response_body);
309 let cup2_urlparam = format!("{public_key_id}:{nonce}");
310
311 let mut hasher = Sha256::new();
312 hasher.update(request_hash);
313 hasher.update(response_hash);
314 hasher.update(cup2_urlparam);
315 hasher.finalize()
316}
317
318impl Cupv2Verifier for StandardCupv2Handler {
319 fn verify_response_with_signature(
320 &self,
321 ecdsa_signature: &DerSignature,
322 request_body: &[u8],
323 response_body: &[u8],
324 public_key_id: PublicKeyId,
325 nonce: &Nonce,
326 ) -> Result<(), CupVerificationError> {
327 let transaction_hash =
328 make_transaction_hash(request_body, response_body, public_key_id, nonce);
329
330 let public_key: &PublicKey = self
331 .parameters_by_id
332 .get(&public_key_id)
333 .ok_or(CupVerificationError::SpecifiedPublicKeyIdMissing)?;
334 let der_signature: ecdsa::der::Signature<p256::NistP256> =
338 ecdsa_signature.as_ref().try_into()?;
339 let signature: ecdsa::Signature<p256::NistP256> = der_signature.try_into()?;
341 Ok(public_key.verify(&transaction_hash, &signature)?)
342 }
343}
344
345fn parse_etag(etag: &str) -> &str {
346 match etag.as_bytes() {
351 [b'W', b'/', b'\"', inner @ .., b'\"'] => unsafe { std::str::from_utf8_unchecked(inner) },
357 [b'\"', inner @ .., b'\"'] => unsafe { std::str::from_utf8_unchecked(inner) },
359 _ => etag,
361 }
362}
363
364pub mod test_support {
365 use super::*;
366 use crate::{
367 protocol::request::{Request, RequestWrapper},
368 request_builder::Intermediate,
369 };
370 use p256::ecdsa::SigningKey;
371 use std::{convert::TryInto, str::FromStr};
372
373 pub const RAW_PRIVATE_KEY_FOR_TEST: &str = include_str!("testing_keys/test_private_key.pem");
374 pub const RAW_PUBLIC_KEY_FOR_TEST: &str = include_str!("testing_keys/test_public_key.pem");
375
376 pub fn make_default_public_key_id_for_test() -> PublicKeyId {
377 123456789.try_into().unwrap()
378 }
379 pub fn make_default_private_key_for_test() -> SigningKey {
380 SigningKey::from_str(RAW_PRIVATE_KEY_FOR_TEST).unwrap()
381 }
382 pub fn make_default_public_key_for_test() -> PublicKey {
383 PublicKey::from_str(RAW_PUBLIC_KEY_FOR_TEST).unwrap()
384 }
385
386 pub fn make_keys_for_test() -> (SigningKey, PublicKey) {
387 (
388 make_default_private_key_for_test(),
389 make_default_public_key_for_test(),
390 )
391 }
392
393 pub fn make_public_keys_for_test(
394 public_key_id: PublicKeyId,
395 public_key: PublicKey,
396 ) -> PublicKeys {
397 PublicKeys {
398 latest: PublicKeyAndId {
399 id: public_key_id,
400 key: public_key,
401 },
402 historical: vec![],
403 }
404 }
405
406 pub fn make_default_public_keys_for_test() -> PublicKeys {
407 let (_priv_key, public_key) = make_keys_for_test();
408 make_public_keys_for_test(make_default_public_key_id_for_test(), public_key)
409 }
410
411 pub fn make_default_json_public_keys_for_test() -> serde_json::Value {
412 serde_json::json!({
413 "latest": {
414 "id": make_default_public_key_id_for_test(),
415 "key": RAW_PUBLIC_KEY_FOR_TEST,
416 },
417 "historical": []
418 })
419 }
420 pub fn make_cup_handler_for_test() -> StandardCupv2Handler {
421 let (_signing_key, public_key) = make_keys_for_test();
422 let public_keys =
423 make_public_keys_for_test(make_default_public_key_id_for_test(), public_key);
424 StandardCupv2Handler::new(&public_keys)
425 }
426
427 pub fn make_expected_signature_for_test(
428 signing_key: &SigningKey,
429 request_metadata: &RequestMetadata,
430 response_body: &[u8],
431 ) -> Vec<u8> {
432 use signature::Signer;
433 let transaction_hash = make_transaction_hash(
434 &request_metadata.request_body,
435 response_body,
436 request_metadata.public_key_id,
437 &request_metadata.nonce,
438 );
439 signing_key
440 .sign(&transaction_hash)
441 .to_der()
442 .as_bytes()
443 .to_vec()
444 }
445
446 pub struct MockCupv2Handler {
448 decoration_error: fn() -> Option<CupDecorationError>,
449 verification_error: fn() -> Option<CupVerificationError>,
450 }
451 impl MockCupv2Handler {
452 pub fn new() -> MockCupv2Handler {
453 MockCupv2Handler {
454 decoration_error: || None::<CupDecorationError>,
455 verification_error: || None::<CupVerificationError>,
456 }
457 }
458 pub fn set_decoration_error(
459 mut self,
460 e: fn() -> Option<CupDecorationError>,
461 ) -> MockCupv2Handler {
462 self.decoration_error = e;
463 self
464 }
465 pub fn set_verification_error(
466 mut self,
467 e: fn() -> Option<CupVerificationError>,
468 ) -> MockCupv2Handler {
469 self.verification_error = e;
470 self
471 }
472 }
473
474 impl Default for MockCupv2Handler {
475 fn default() -> Self {
476 Self::new()
477 }
478 }
479
480 impl Cupv2RequestHandler for MockCupv2Handler {
481 fn decorate_request(
482 &self,
483 _request: &mut impl CupRequest,
484 ) -> Result<RequestMetadata, CupDecorationError> {
485 match (self.decoration_error)() {
486 Some(e) => Err(e),
487 None => Ok(RequestMetadata {
488 request_body: vec![],
489 public_key_id: 0.try_into().unwrap(),
490 nonce: [0u8; 32].into(),
491 }),
492 }
493 }
494
495 fn verify_response(
496 &self,
497 request_metadata: &RequestMetadata,
498 resp: &Response<Vec<u8>>,
499 public_key_id: PublicKeyId,
500 ) -> Result<DerSignature, CupVerificationError> {
501 use rand::rngs::OsRng;
502 let signing_key = SigningKey::random(&mut OsRng);
503 let signature = DerSignature::from_bytes(&make_expected_signature_for_test(
504 &signing_key,
505 request_metadata,
506 resp.body(),
507 ))
508 .unwrap();
509 let () = self.verify_response_with_signature(
510 &signature,
511 &request_metadata.request_body,
512 resp.body(),
513 public_key_id,
514 &request_metadata.nonce,
515 )?;
516 Ok(signature)
517 }
518 }
519
520 impl Cupv2Verifier for MockCupv2Handler {
521 fn verify_response_with_signature(
522 &self,
523 _ecdsa_signature: &DerSignature,
524 _request_body: &[u8],
525 _response_body: &[u8],
526 _public_key_id: PublicKeyId,
527 _nonce: &Nonce,
528 ) -> Result<(), CupVerificationError> {
529 match (self.verification_error)() {
530 Some(e) => Err(e),
531 None => Ok(()),
532 }
533 }
534 }
535
536 pub fn make_standard_intermediate_for_test(request: Request) -> Intermediate {
537 Intermediate {
538 uri: "http://fuchsia.dev".to_string(),
539 headers: [].into(),
540 body: RequestWrapper { request },
541 }
542 }
543}
544
545#[cfg(test)]
546mod tests {
547 use super::*;
548 use crate::{
549 protocol::request::{Request, RequestWrapper},
550 request_builder::Intermediate,
551 };
552 use assert_matches::assert_matches;
553 use p256::ecdsa::SigningKey;
554 use url::Url;
555
556 impl PartialEq for CupVerificationError {
558 fn eq(&self, other: &Self) -> bool {
559 format!("{self:?}") == format!("{other:?}")
560 }
561 }
562
563 #[test]
564 fn test_standard_cup_handler_decorate() -> Result<(), anyhow::Error> {
565 let (_, public_key) = test_support::make_keys_for_test();
566 let public_key_id: PublicKeyId = 42.try_into()?;
567 let public_keys = test_support::make_public_keys_for_test(public_key_id, public_key);
568 let cup_handler = StandardCupv2Handler::new(&public_keys);
569
570 let mut intermediate =
571 test_support::make_standard_intermediate_for_test(Request::default());
572
573 let request_metadata = cup_handler.decorate_request(&mut intermediate)?;
574
575 let cup2key_value: String = Url::parse(&intermediate.uri)?
577 .query_pairs()
578 .find_map(|(k, v)| if k == "cup2key" { Some(v) } else { None })
579 .unwrap()
580 .to_string();
581
582 let (public_key_decimal, nonce_hex) = cup2key_value.split_once(':').unwrap();
583 assert_eq!(public_key_decimal, public_key_id.to_string());
584 assert_eq!(nonce_hex, request_metadata.nonce.to_string());
585 assert_ne!(request_metadata.nonce, [0_u8; 32].into());
587
588 Ok(())
589 }
590
591 #[test]
592 fn test_standard_cup_handler_decorate_ipv6_link_local() -> Result<(), anyhow::Error> {
593 let (_, public_key) = test_support::make_keys_for_test();
594 let public_key_id: PublicKeyId = 42.try_into()?;
595 let public_keys = test_support::make_public_keys_for_test(public_key_id, public_key);
596 let cup_handler = StandardCupv2Handler::new(&public_keys);
597
598 let mut intermediate = Intermediate {
599 uri: "http://[::1%eth0]".to_string(),
600 headers: [].into(),
601 body: RequestWrapper {
602 request: Request::default(),
603 },
604 };
605
606 let request_metadata = cup_handler.decorate_request(&mut intermediate)?;
607
608 assert_eq!(
609 intermediate.uri,
610 format!(
611 "http://[::1%eth0]/?cup2key={}:{}",
612 public_key_id, request_metadata.nonce,
613 )
614 );
615
616 assert_ne!(request_metadata.nonce, [0_u8; 32].into());
618
619 Ok(())
620 }
621
622 #[test]
623 fn test_verify_response_missing_etag_header() -> Result<(), anyhow::Error> {
624 let (_, public_key) = test_support::make_keys_for_test();
625 let public_key_id: PublicKeyId = 12345.try_into()?;
626 let public_keys = test_support::make_public_keys_for_test(public_key_id, public_key);
627 let cup_handler = StandardCupv2Handler::new(&public_keys);
628
629 let mut intermediate =
630 test_support::make_standard_intermediate_for_test(Request::default());
631 let request_metadata = cup_handler.decorate_request(&mut intermediate)?;
632
633 let response: Response<Vec<u8>> = hyper::Response::builder()
635 .status(200)
636 .body("foo".as_bytes().to_vec())?;
637
638 assert_matches!(
639 cup_handler.verify_response(&request_metadata, &response, public_key_id),
640 Err(CupVerificationError::EtagHeaderMissing)
641 );
642 Ok(())
643 }
644
645 #[test]
646 fn test_verify_response_malformed_etag_header() -> Result<(), anyhow::Error> {
647 let (_, public_key) = test_support::make_keys_for_test();
648 let public_key_id: PublicKeyId = 12345.try_into()?;
649 let public_keys = test_support::make_public_keys_for_test(public_key_id, public_key);
650 let cup_handler = StandardCupv2Handler::new(&public_keys);
651
652 let mut intermediate =
653 test_support::make_standard_intermediate_for_test(Request::default());
654 let request_metadata = cup_handler.decorate_request(&mut intermediate)?;
655
656 let response: Response<Vec<u8>> = hyper::Response::builder()
657 .status(200)
658 .header(ETAG, "\u{FEFF}")
659 .body("foo".as_bytes().to_vec())?;
660
661 assert_matches!(
662 cup_handler.verify_response(&request_metadata, &response, public_key_id),
663 Err(CupVerificationError::EtagNotString(_))
664 );
665 Ok(())
666 }
667
668 #[test]
669 fn test_verify_cached_signature_against_message() -> Result<(), anyhow::Error> {
670 let (priv_key, public_key) = test_support::make_keys_for_test();
671 let response_body = "bar";
672 let correct_public_key_id: PublicKeyId = 24682468.try_into()?;
673 let wrong_public_key_id: PublicKeyId = 12341234.try_into()?;
674
675 let public_keys =
676 test_support::make_public_keys_for_test(correct_public_key_id, public_key);
677 let cup_handler = StandardCupv2Handler::new(&public_keys);
678 let mut intermediate =
679 test_support::make_standard_intermediate_for_test(Request::default());
680 let request_metadata = cup_handler.decorate_request(&mut intermediate)?;
681 let expected_request_metadata = RequestMetadata {
682 request_body: intermediate.serialize_body()?,
683 public_key_id: correct_public_key_id,
684 nonce: request_metadata.nonce,
685 };
686
687 let expected_hash = Sha256::digest(&request_metadata.request_body);
688
689 let expected_hash_hex: String = hex::encode(expected_hash);
690 let expected_signature = hex::encode(test_support::make_expected_signature_for_test(
691 &priv_key,
692 &expected_request_metadata,
693 response_body.as_bytes(),
694 ));
695
696 for (etag, public_key_id, expected_err) in vec![
697 (
699 "bar",
700 correct_public_key_id,
701 Some(CupVerificationError::EtagMalformed),
702 ),
703 (
705 "foo:bar",
706 correct_public_key_id,
707 Some(CupVerificationError::RequestHashMalformed),
708 ),
709 (
711 &format!("foo:{}", hex::encode([1; 32])),
712 correct_public_key_id,
713 Some(CupVerificationError::RequestHashMismatch),
714 ),
715 (
718 &format!("foo:{expected_hash_hex}"),
719 correct_public_key_id,
720 Some(CupVerificationError::SignatureMalformed),
721 ),
722 (
725 &format!("{}:{}", hex::encode([1; 64]), expected_hash_hex),
726 correct_public_key_id,
727 Some(CupVerificationError::SignatureError(ecdsa::Error::new())),
728 ),
729 (
731 &format!("{expected_signature}:{expected_hash_hex}",),
732 wrong_public_key_id,
733 Some(CupVerificationError::SpecifiedPublicKeyIdMissing),
734 ),
735 (
737 &format!("{expected_signature}:{expected_hash_hex}",),
738 correct_public_key_id,
739 None,
740 ),
741 ] {
742 let response: Response<Vec<u8>> = hyper::Response::builder()
743 .status(200)
744 .header(ETAG, etag)
745 .body(response_body.as_bytes().to_vec())?;
746 let actual_err = cup_handler
747 .verify_response(&request_metadata, &response, public_key_id)
748 .err();
749 assert_eq!(
750 actual_err, expected_err,
751 "Received error {actual_err:?}, expected error {expected_err:?}"
752 );
753 }
754
755 Ok(())
756 }
757
758 fn make_verify_response_arguments(
762 request_handler: &impl Cupv2RequestHandler,
763 private_key: SigningKey,
764 response_body: &str,
765 ) -> Result<(RequestMetadata, Response<Vec<u8>>), anyhow::Error> {
766 let mut intermediate =
767 test_support::make_standard_intermediate_for_test(Request::default());
768 let request_metadata = request_handler.decorate_request(&mut intermediate)?;
769
770 let signature = hex::encode(test_support::make_expected_signature_for_test(
771 &private_key,
772 &request_metadata,
773 response_body.as_bytes(),
774 ));
775
776 let etag = &format!(
777 "{}:{}",
778 signature,
779 hex::encode(Sha256::digest(&request_metadata.request_body))
780 );
781
782 let response: Response<Vec<u8>> = hyper::Response::builder()
783 .status(200)
784 .header(ETAG, etag)
785 .body(response_body.as_bytes().to_vec())?;
786 Ok((request_metadata, response))
787 }
788
789 #[test]
790 fn test_historical_verification() -> Result<(), anyhow::Error> {
791 let (private_key_a, public_key_a) = test_support::make_keys_for_test();
792 let public_key_id_a: PublicKeyId = 24682468.try_into()?;
793 let response_body_a = "foo";
794
795 let public_keys = PublicKeys {
796 latest: PublicKeyAndId {
797 id: public_key_id_a,
798 key: public_key_a,
799 },
800 historical: vec![],
801 };
802 let mut cup_handler = StandardCupv2Handler::new(&public_keys);
803 let (request_metadata_a, response_a) =
804 make_verify_response_arguments(&cup_handler, private_key_a, response_body_a)?;
805 assert_matches!(
806 cup_handler.verify_response(&request_metadata_a, &response_a, public_key_id_a),
807 Ok(_)
808 );
809
810 let (private_key_b, public_key_b) = test_support::make_keys_for_test();
812 let public_key_id_b: PublicKeyId = 12341234.try_into()?;
813 let response_body_b = "bar";
814
815 let public_keys = PublicKeys {
817 latest: PublicKeyAndId {
818 id: public_key_id_b,
819 key: public_key_b,
820 },
821 historical: vec![PublicKeyAndId {
822 id: public_key_id_a,
823 key: public_key_a,
824 }],
825 };
826 cup_handler = StandardCupv2Handler::new(&public_keys);
827
828 let (request_metadata_b, response_b) =
829 make_verify_response_arguments(&cup_handler, private_key_b, response_body_b)?;
830 assert_matches!(
832 cup_handler.verify_response(&request_metadata_b, &response_b, public_key_id_b),
833 Ok(_)
834 );
835
836 assert_matches!(
838 cup_handler.verify_response(&request_metadata_a, &response_a, public_key_id_a),
839 Ok(_)
840 );
841
842 assert!(cup_handler
845 .verify_response(&request_metadata_a, &response_a, public_key_id_b)
846 .is_err());
847 assert!(cup_handler
848 .verify_response(&request_metadata_a, &response_b, public_key_id_a)
849 .is_err());
850 assert!(cup_handler
851 .verify_response(&request_metadata_b, &response_a, public_key_id_a)
852 .is_err());
853
854 Ok(())
855 }
856
857 #[test]
858 fn test_deserialize_public_keys() {
859 let public_key_and_id: PublicKeyAndId = serde_json::from_value(serde_json::json!(
860 {
861 "id": 123,
862 "key": test_support::RAW_PUBLIC_KEY_FOR_TEST,
863 }
864 ))
865 .unwrap();
866
867 assert_eq!(
868 public_key_and_id.key,
869 test_support::make_default_public_key_for_test()
870 );
871 }
872
873 #[test]
874 fn test_publickeys_roundtrip() {
875 let public_keys = test_support::make_default_public_keys_for_test();
878 let public_keys_serialized = serde_json::to_string(&public_keys).unwrap();
879 let public_keys_reconstituted = serde_json::from_str(&public_keys_serialized).unwrap();
880 assert_eq!(public_keys, public_keys_reconstituted);
881 }
882
883 #[test]
884 fn test_parse_etag() {
885 assert_eq!(parse_etag("W/\"foo\""), "foo");
887 assert_eq!(
888 parse_etag("W/\"thing-\"with\"-quotes\""),
889 "thing-\"with\"-quotes"
890 );
891 assert_eq!(parse_etag("W/\"\""), "");
892 assert_eq!(parse_etag("\"foo\""), "foo");
894 assert_eq!(
895 parse_etag("\"thing-\"with\"-quotes\""),
896 "thing-\"with\"-quotes",
897 );
898 assert_eq!(parse_etag("\"\""), "");
899 for v in [
901 "foo",
902 "1",
903 "W",
904 "W\"",
905 "W/\"",
906 "W/",
907 "w/\"bar\"",
908 "W/'bar'",
909 "",
910 ] {
911 assert_eq!(parse_etag(v), v);
913 }
914 }
915}