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)]
139pub struct RequestMetadata {
140 pub request_body: Vec<u8>,
141 pub public_key_id: PublicKeyId,
142 pub nonce: Nonce,
143}
144
145pub trait CupRequest {
148 fn get_uri(&self) -> &str;
150 fn set_uri(&mut self, uri: String);
152 fn get_serialized_body(&self) -> serde_json::Result<Vec<u8>>;
154}
155
156pub trait Cupv2RequestHandler {
159 fn decorate_request(
163 &self,
164 request: &mut impl CupRequest,
165 ) -> Result<RequestMetadata, CupDecorationError>;
166
167 fn verify_response(
170 &self,
171 request_metadata: &RequestMetadata,
172 resp: &Response<Vec<u8>>,
173 public_key_id: PublicKeyId,
174 ) -> Result<DerSignature, CupVerificationError>;
175}
176
177pub trait Cupv2Verifier {
179 fn verify_response_with_signature(
182 &self,
183 ecdsa_signature: &DerSignature,
184 request_body: &[u8],
185 response_body: &[u8],
186 public_key_id: PublicKeyId,
187 nonce: &Nonce,
188 ) -> Result<(), CupVerificationError>;
189}
190
191pub trait Cupv2Handler: Cupv2RequestHandler + Cupv2Verifier {}
192
193impl<T> Cupv2Handler for T where T: Cupv2RequestHandler + Cupv2Verifier {}
194
195#[derive(Debug)]
197pub struct StandardCupv2Handler {
198 parameters_by_id: HashMap<PublicKeyId, PublicKey>,
201 latest_public_key_id: PublicKeyId,
202}
203
204impl StandardCupv2Handler {
205 pub fn new(public_keys: &PublicKeys) -> Self {
207 Self {
208 parameters_by_id: std::iter::once(&public_keys.latest)
209 .chain(&public_keys.historical)
210 .map(|k| (k.id, k.key))
211 .collect(),
212 latest_public_key_id: public_keys.latest.id,
213 }
214 }
215}
216
217impl Cupv2RequestHandler for StandardCupv2Handler {
218 fn decorate_request(
219 &self,
220 request: &mut impl CupRequest,
221 ) -> Result<RequestMetadata, CupDecorationError> {
222 let public_key_id: PublicKeyId = self.latest_public_key_id;
230
231 let nonce = Nonce::new();
232
233 let uri: Uri = request.get_uri().parse()?;
234 let uri = uri.append_query_parameter("cup2key", &format!("{public_key_id}:{nonce}"))?;
235 request.set_uri(uri.to_string());
236
237 Ok(RequestMetadata {
238 request_body: request.get_serialized_body()?,
239 public_key_id,
240 nonce,
241 })
242 }
243
244 fn verify_response(
245 &self,
246 request_metadata: &RequestMetadata,
247 resp: &Response<Vec<u8>>,
248 public_key_id: PublicKeyId,
249 ) -> Result<DerSignature, CupVerificationError> {
250 let etag_header = resp
266 .headers()
267 .get(ETAG)
268 .ok_or(CupVerificationError::EtagHeaderMissing)?
269 .to_str()
270 .map_err(CupVerificationError::EtagNotString)
271 .map(parse_etag)?;
272
273 let (encoded_signature, hex_hash): (&str, &str) = etag_header
274 .split_once(':')
275 .ok_or(CupVerificationError::EtagMalformed)?;
276
277 let actual_hash =
278 &hex::decode(hex_hash).map_err(|_| CupVerificationError::RequestHashMalformed)?;
279
280 let request_body_hash = Sha256::digest(&request_metadata.request_body);
281 if *request_body_hash != *actual_hash {
282 return Err(CupVerificationError::RequestHashMismatch);
283 }
284
285 let signature = DerSignature::from_bytes(
286 &hex::decode(encoded_signature)
287 .map_err(|_| CupVerificationError::SignatureMalformed)?,
288 )?;
289
290 let () = self.verify_response_with_signature(
291 &signature,
292 &request_metadata.request_body,
293 resp.body(),
294 public_key_id,
295 &request_metadata.nonce,
296 )?;
297
298 Ok(signature)
299 }
300}
301
302pub fn make_transaction_hash(
303 request_body: &[u8],
304 response_body: &[u8],
305 public_key_id: PublicKeyId,
306 nonce: &Nonce,
307) -> digest::Output<Sha256> {
308 let request_hash = Sha256::digest(request_body);
309 let response_hash = Sha256::digest(response_body);
310 let cup2_urlparam = format!("{public_key_id}:{nonce}");
311
312 let mut hasher = Sha256::new();
313 hasher.update(request_hash);
314 hasher.update(response_hash);
315 hasher.update(cup2_urlparam);
316 hasher.finalize()
317}
318
319impl Cupv2Verifier for StandardCupv2Handler {
320 fn verify_response_with_signature(
321 &self,
322 ecdsa_signature: &DerSignature,
323 request_body: &[u8],
324 response_body: &[u8],
325 public_key_id: PublicKeyId,
326 nonce: &Nonce,
327 ) -> Result<(), CupVerificationError> {
328 let transaction_hash =
329 make_transaction_hash(request_body, response_body, public_key_id, nonce);
330
331 let public_key: &PublicKey = self
332 .parameters_by_id
333 .get(&public_key_id)
334 .ok_or(CupVerificationError::SpecifiedPublicKeyIdMissing)?;
335 let der_signature: ecdsa::der::Signature<p256::NistP256> =
339 ecdsa_signature.as_ref().try_into()?;
340 let signature: ecdsa::Signature<p256::NistP256> = der_signature.try_into()?;
342 Ok(public_key.verify(&transaction_hash, &signature)?)
343 }
344}
345
346fn parse_etag(etag: &str) -> &str {
347 match etag.as_bytes() {
352 [b'W', b'/', b'\"', inner @ .., b'\"'] => unsafe { std::str::from_utf8_unchecked(inner) },
358 [b'\"', inner @ .., b'\"'] => unsafe { std::str::from_utf8_unchecked(inner) },
360 _ => etag,
362 }
363}
364
365pub mod test_support {
366 use super::*;
367 use crate::{
368 protocol::request::{Request, RequestWrapper},
369 request_builder::Intermediate,
370 };
371 use p256::ecdsa::SigningKey;
372 use std::{convert::TryInto, str::FromStr};
373
374 pub const RAW_PRIVATE_KEY_FOR_TEST: &str = include_str!("testing_keys/test_private_key.pem");
375 pub const RAW_PUBLIC_KEY_FOR_TEST: &str = include_str!("testing_keys/test_public_key.pem");
376
377 pub fn make_default_public_key_id_for_test() -> PublicKeyId {
378 123456789.try_into().unwrap()
379 }
380 pub fn make_default_private_key_for_test() -> SigningKey {
381 SigningKey::from_str(RAW_PRIVATE_KEY_FOR_TEST).unwrap()
382 }
383 pub fn make_default_public_key_for_test() -> PublicKey {
384 PublicKey::from_str(RAW_PUBLIC_KEY_FOR_TEST).unwrap()
385 }
386
387 pub fn make_keys_for_test() -> (SigningKey, PublicKey) {
388 (
389 make_default_private_key_for_test(),
390 make_default_public_key_for_test(),
391 )
392 }
393
394 pub fn make_public_keys_for_test(
395 public_key_id: PublicKeyId,
396 public_key: PublicKey,
397 ) -> PublicKeys {
398 PublicKeys {
399 latest: PublicKeyAndId {
400 id: public_key_id,
401 key: public_key,
402 },
403 historical: vec![],
404 }
405 }
406
407 pub fn make_default_public_keys_for_test() -> PublicKeys {
408 let (_priv_key, public_key) = make_keys_for_test();
409 make_public_keys_for_test(make_default_public_key_id_for_test(), public_key)
410 }
411
412 pub fn make_default_json_public_keys_for_test() -> serde_json::Value {
413 serde_json::json!({
414 "latest": {
415 "id": make_default_public_key_id_for_test(),
416 "key": RAW_PUBLIC_KEY_FOR_TEST,
417 },
418 "historical": []
419 })
420 }
421 pub fn make_cup_handler_for_test() -> StandardCupv2Handler {
422 let (_signing_key, public_key) = make_keys_for_test();
423 let public_keys =
424 make_public_keys_for_test(make_default_public_key_id_for_test(), public_key);
425 StandardCupv2Handler::new(&public_keys)
426 }
427
428 pub fn make_expected_signature_for_test(
429 signing_key: &SigningKey,
430 request_metadata: &RequestMetadata,
431 response_body: &[u8],
432 ) -> Vec<u8> {
433 use signature::Signer;
434 let transaction_hash = make_transaction_hash(
435 &request_metadata.request_body,
436 response_body,
437 request_metadata.public_key_id,
438 &request_metadata.nonce,
439 );
440 signing_key
441 .sign(&transaction_hash)
442 .to_der()
443 .as_bytes()
444 .to_vec()
445 }
446
447 pub struct MockCupv2Handler {
449 decoration_error: fn() -> Option<CupDecorationError>,
450 verification_error: fn() -> Option<CupVerificationError>,
451 }
452 impl MockCupv2Handler {
453 pub fn new() -> MockCupv2Handler {
454 MockCupv2Handler {
455 decoration_error: || None::<CupDecorationError>,
456 verification_error: || None::<CupVerificationError>,
457 }
458 }
459 pub fn set_decoration_error(
460 mut self,
461 e: fn() -> Option<CupDecorationError>,
462 ) -> MockCupv2Handler {
463 self.decoration_error = e;
464 self
465 }
466 pub fn set_verification_error(
467 mut self,
468 e: fn() -> Option<CupVerificationError>,
469 ) -> MockCupv2Handler {
470 self.verification_error = e;
471 self
472 }
473 }
474
475 impl Default for MockCupv2Handler {
476 fn default() -> Self {
477 Self::new()
478 }
479 }
480
481 impl Cupv2RequestHandler for MockCupv2Handler {
482 fn decorate_request(
483 &self,
484 _request: &mut impl CupRequest,
485 ) -> Result<RequestMetadata, CupDecorationError> {
486 match (self.decoration_error)() {
487 Some(e) => Err(e),
488 None => Ok(RequestMetadata {
489 request_body: vec![],
490 public_key_id: 0.try_into().unwrap(),
491 nonce: [0u8; 32].into(),
492 }),
493 }
494 }
495
496 fn verify_response(
497 &self,
498 request_metadata: &RequestMetadata,
499 resp: &Response<Vec<u8>>,
500 public_key_id: PublicKeyId,
501 ) -> Result<DerSignature, CupVerificationError> {
502 use rand::rngs::OsRng;
503 let signing_key = SigningKey::random(&mut OsRng);
504 let signature = DerSignature::from_bytes(&make_expected_signature_for_test(
505 &signing_key,
506 request_metadata,
507 resp.body(),
508 ))
509 .unwrap();
510 let () = self.verify_response_with_signature(
511 &signature,
512 &request_metadata.request_body,
513 resp.body(),
514 public_key_id,
515 &request_metadata.nonce,
516 )?;
517 Ok(signature)
518 }
519 }
520
521 impl Cupv2Verifier for MockCupv2Handler {
522 fn verify_response_with_signature(
523 &self,
524 _ecdsa_signature: &DerSignature,
525 _request_body: &[u8],
526 _response_body: &[u8],
527 _public_key_id: PublicKeyId,
528 _nonce: &Nonce,
529 ) -> Result<(), CupVerificationError> {
530 match (self.verification_error)() {
531 Some(e) => Err(e),
532 None => Ok(()),
533 }
534 }
535 }
536
537 pub fn make_standard_intermediate_for_test(request: Request) -> Intermediate {
538 Intermediate {
539 uri: "http://fuchsia.dev".to_string(),
540 headers: [].into(),
541 body: RequestWrapper { request },
542 }
543 }
544}
545
546#[cfg(test)]
547mod tests {
548 use super::*;
549 use crate::{
550 protocol::request::{Request, RequestWrapper},
551 request_builder::Intermediate,
552 };
553 use assert_matches::assert_matches;
554 use p256::ecdsa::SigningKey;
555 use url::Url;
556
557 impl PartialEq for CupVerificationError {
559 fn eq(&self, other: &Self) -> bool {
560 format!("{self:?}") == format!("{other:?}")
561 }
562 }
563
564 #[test]
565 fn test_standard_cup_handler_decorate() -> Result<(), anyhow::Error> {
566 let (_, public_key) = test_support::make_keys_for_test();
567 let public_key_id: PublicKeyId = 42.try_into()?;
568 let public_keys = test_support::make_public_keys_for_test(public_key_id, public_key);
569 let cup_handler = StandardCupv2Handler::new(&public_keys);
570
571 let mut intermediate =
572 test_support::make_standard_intermediate_for_test(Request::default());
573
574 let request_metadata = cup_handler.decorate_request(&mut intermediate)?;
575
576 let cup2key_value: String = Url::parse(&intermediate.uri)?
578 .query_pairs()
579 .find_map(|(k, v)| if k == "cup2key" { Some(v) } else { None })
580 .unwrap()
581 .to_string();
582
583 let (public_key_decimal, nonce_hex) = cup2key_value.split_once(':').unwrap();
584 assert_eq!(public_key_decimal, public_key_id.to_string());
585 assert_eq!(nonce_hex, request_metadata.nonce.to_string());
586 assert_ne!(request_metadata.nonce, [0_u8; 32].into());
588
589 Ok(())
590 }
591
592 #[test]
593 fn test_standard_cup_handler_decorate_ipv6_link_local() -> Result<(), anyhow::Error> {
594 let (_, public_key) = test_support::make_keys_for_test();
595 let public_key_id: PublicKeyId = 42.try_into()?;
596 let public_keys = test_support::make_public_keys_for_test(public_key_id, public_key);
597 let cup_handler = StandardCupv2Handler::new(&public_keys);
598
599 let mut intermediate = Intermediate {
600 uri: "http://[::1%eth0]".to_string(),
601 headers: [].into(),
602 body: RequestWrapper {
603 request: Request::default(),
604 },
605 };
606
607 let request_metadata = cup_handler.decorate_request(&mut intermediate)?;
608
609 assert_eq!(
610 intermediate.uri,
611 format!(
612 "http://[::1%eth0]/?cup2key={}:{}",
613 public_key_id, request_metadata.nonce,
614 )
615 );
616
617 assert_ne!(request_metadata.nonce, [0_u8; 32].into());
619
620 Ok(())
621 }
622
623 #[test]
624 fn test_verify_response_missing_etag_header() -> Result<(), anyhow::Error> {
625 let (_, public_key) = test_support::make_keys_for_test();
626 let public_key_id: PublicKeyId = 12345.try_into()?;
627 let public_keys = test_support::make_public_keys_for_test(public_key_id, public_key);
628 let cup_handler = StandardCupv2Handler::new(&public_keys);
629
630 let mut intermediate =
631 test_support::make_standard_intermediate_for_test(Request::default());
632 let request_metadata = cup_handler.decorate_request(&mut intermediate)?;
633
634 let response: Response<Vec<u8>> = hyper::Response::builder()
636 .status(200)
637 .body("foo".as_bytes().to_vec())?;
638
639 assert_matches!(
640 cup_handler.verify_response(&request_metadata, &response, public_key_id),
641 Err(CupVerificationError::EtagHeaderMissing)
642 );
643 Ok(())
644 }
645
646 #[test]
647 fn test_verify_response_malformed_etag_header() -> Result<(), anyhow::Error> {
648 let (_, public_key) = test_support::make_keys_for_test();
649 let public_key_id: PublicKeyId = 12345.try_into()?;
650 let public_keys = test_support::make_public_keys_for_test(public_key_id, public_key);
651 let cup_handler = StandardCupv2Handler::new(&public_keys);
652
653 let mut intermediate =
654 test_support::make_standard_intermediate_for_test(Request::default());
655 let request_metadata = cup_handler.decorate_request(&mut intermediate)?;
656
657 let response: Response<Vec<u8>> = hyper::Response::builder()
658 .status(200)
659 .header(ETAG, "\u{FEFF}")
660 .body("foo".as_bytes().to_vec())?;
661
662 assert_matches!(
663 cup_handler.verify_response(&request_metadata, &response, public_key_id),
664 Err(CupVerificationError::EtagNotString(_))
665 );
666 Ok(())
667 }
668
669 #[test]
670 fn test_verify_cached_signature_against_message() -> Result<(), anyhow::Error> {
671 let (priv_key, public_key) = test_support::make_keys_for_test();
672 let response_body = "bar";
673 let correct_public_key_id: PublicKeyId = 24682468.try_into()?;
674 let wrong_public_key_id: PublicKeyId = 12341234.try_into()?;
675
676 let public_keys =
677 test_support::make_public_keys_for_test(correct_public_key_id, public_key);
678 let cup_handler = StandardCupv2Handler::new(&public_keys);
679 let mut intermediate =
680 test_support::make_standard_intermediate_for_test(Request::default());
681 let request_metadata = cup_handler.decorate_request(&mut intermediate)?;
682 let expected_request_metadata = RequestMetadata {
683 request_body: intermediate.serialize_body()?,
684 public_key_id: correct_public_key_id,
685 nonce: request_metadata.nonce,
686 };
687
688 let expected_hash = Sha256::digest(&request_metadata.request_body);
689
690 let expected_hash_hex: String = hex::encode(expected_hash);
691 let expected_signature = hex::encode(test_support::make_expected_signature_for_test(
692 &priv_key,
693 &expected_request_metadata,
694 response_body.as_bytes(),
695 ));
696
697 for (etag, public_key_id, expected_err) in vec![
698 (
700 "bar",
701 correct_public_key_id,
702 Some(CupVerificationError::EtagMalformed),
703 ),
704 (
706 "foo:bar",
707 correct_public_key_id,
708 Some(CupVerificationError::RequestHashMalformed),
709 ),
710 (
712 &format!("foo:{}", hex::encode([1; 32])),
713 correct_public_key_id,
714 Some(CupVerificationError::RequestHashMismatch),
715 ),
716 (
719 &format!("foo:{expected_hash_hex}"),
720 correct_public_key_id,
721 Some(CupVerificationError::SignatureMalformed),
722 ),
723 (
726 &format!("{}:{}", hex::encode([1; 64]), expected_hash_hex),
727 correct_public_key_id,
728 Some(CupVerificationError::SignatureError(ecdsa::Error::new())),
729 ),
730 (
732 &format!("{expected_signature}:{expected_hash_hex}",),
733 wrong_public_key_id,
734 Some(CupVerificationError::SpecifiedPublicKeyIdMissing),
735 ),
736 (
738 &format!("{expected_signature}:{expected_hash_hex}",),
739 correct_public_key_id,
740 None,
741 ),
742 ] {
743 let response: Response<Vec<u8>> = hyper::Response::builder()
744 .status(200)
745 .header(ETAG, etag)
746 .body(response_body.as_bytes().to_vec())?;
747 let actual_err = cup_handler
748 .verify_response(&request_metadata, &response, public_key_id)
749 .err();
750 assert_eq!(
751 actual_err, expected_err,
752 "Received error {actual_err:?}, expected error {expected_err:?}"
753 );
754 }
755
756 Ok(())
757 }
758
759 fn make_verify_response_arguments(
763 request_handler: &impl Cupv2RequestHandler,
764 private_key: SigningKey,
765 response_body: &str,
766 ) -> Result<(RequestMetadata, Response<Vec<u8>>), anyhow::Error> {
767 let mut intermediate =
768 test_support::make_standard_intermediate_for_test(Request::default());
769 let request_metadata = request_handler.decorate_request(&mut intermediate)?;
770
771 let signature = hex::encode(test_support::make_expected_signature_for_test(
772 &private_key,
773 &request_metadata,
774 response_body.as_bytes(),
775 ));
776
777 let etag = &format!(
778 "{}:{}",
779 signature,
780 hex::encode(Sha256::digest(&request_metadata.request_body))
781 );
782
783 let response: Response<Vec<u8>> = hyper::Response::builder()
784 .status(200)
785 .header(ETAG, etag)
786 .body(response_body.as_bytes().to_vec())?;
787 Ok((request_metadata, response))
788 }
789
790 #[test]
791 fn test_historical_verification() -> Result<(), anyhow::Error> {
792 let (private_key_a, public_key_a) = test_support::make_keys_for_test();
793 let public_key_id_a: PublicKeyId = 24682468.try_into()?;
794 let response_body_a = "foo";
795
796 let public_keys = PublicKeys {
797 latest: PublicKeyAndId {
798 id: public_key_id_a,
799 key: public_key_a,
800 },
801 historical: vec![],
802 };
803 let mut cup_handler = StandardCupv2Handler::new(&public_keys);
804 let (request_metadata_a, response_a) =
805 make_verify_response_arguments(&cup_handler, private_key_a, response_body_a)?;
806 assert_matches!(
807 cup_handler.verify_response(&request_metadata_a, &response_a, public_key_id_a),
808 Ok(_)
809 );
810
811 let (private_key_b, public_key_b) = test_support::make_keys_for_test();
813 let public_key_id_b: PublicKeyId = 12341234.try_into()?;
814 let response_body_b = "bar";
815
816 let public_keys = PublicKeys {
818 latest: PublicKeyAndId {
819 id: public_key_id_b,
820 key: public_key_b,
821 },
822 historical: vec![PublicKeyAndId {
823 id: public_key_id_a,
824 key: public_key_a,
825 }],
826 };
827 cup_handler = StandardCupv2Handler::new(&public_keys);
828
829 let (request_metadata_b, response_b) =
830 make_verify_response_arguments(&cup_handler, private_key_b, response_body_b)?;
831 assert_matches!(
833 cup_handler.verify_response(&request_metadata_b, &response_b, public_key_id_b),
834 Ok(_)
835 );
836
837 assert_matches!(
839 cup_handler.verify_response(&request_metadata_a, &response_a, public_key_id_a),
840 Ok(_)
841 );
842
843 assert!(cup_handler
846 .verify_response(&request_metadata_a, &response_a, public_key_id_b)
847 .is_err());
848 assert!(cup_handler
849 .verify_response(&request_metadata_a, &response_b, public_key_id_a)
850 .is_err());
851 assert!(cup_handler
852 .verify_response(&request_metadata_b, &response_a, public_key_id_a)
853 .is_err());
854
855 Ok(())
856 }
857
858 #[test]
859 fn test_deserialize_public_keys() {
860 let public_key_and_id: PublicKeyAndId = serde_json::from_value(serde_json::json!(
861 {
862 "id": 123,
863 "key": test_support::RAW_PUBLIC_KEY_FOR_TEST,
864 }
865 ))
866 .unwrap();
867
868 assert_eq!(
869 public_key_and_id.key,
870 test_support::make_default_public_key_for_test()
871 );
872 }
873
874 #[test]
875 fn test_publickeys_roundtrip() {
876 let public_keys = test_support::make_default_public_keys_for_test();
879 let public_keys_serialized = serde_json::to_string(&public_keys).unwrap();
880 let public_keys_reconstituted = serde_json::from_str(&public_keys_serialized).unwrap();
881 assert_eq!(public_keys, public_keys_reconstituted);
882 }
883
884 #[test]
885 fn test_parse_etag() {
886 assert_eq!(parse_etag("W/\"foo\""), "foo");
888 assert_eq!(
889 parse_etag("W/\"thing-\"with\"-quotes\""),
890 "thing-\"with\"-quotes"
891 );
892 assert_eq!(parse_etag("W/\"\""), "");
893 assert_eq!(parse_etag("\"foo\""), "foo");
895 assert_eq!(
896 parse_etag("\"thing-\"with\"-quotes\""),
897 "thing-\"with\"-quotes",
898 );
899 assert_eq!(parse_etag("\"\""), "");
900 for v in [
902 "foo",
903 "1",
904 "W",
905 "W\"",
906 "W/\"",
907 "W/",
908 "w/\"bar\"",
909 "W/'bar'",
910 "",
911 ] {
912 assert_eq!(parse_etag(v), v);
914 }
915 }
916}