1#![warn(missing_docs)]
20#![doc(
21 html_logo_url = "https://raw.githubusercontent.com/getsentry/relay/master/artwork/relay-icon.png",
22 html_favicon_url = "https://raw.githubusercontent.com/getsentry/relay/master/artwork/relay-icon.png"
23)]
24
25use std::fmt;
26use std::fmt::Display;
27use std::str::FromStr;
28
29use chrono::{DateTime, Duration, Utc};
30use data_encoding::BASE64URL_NOPAD;
31use ed25519_dalek::pkcs8::{DecodePrivateKey as _, DecodePublicKey as _};
32use ed25519_dalek::{Digest, DigestSigner, DigestVerifier, Signer, Verifier};
33use hmac::{Hmac, Mac};
34use rand::rngs::OsRng;
35use rand::{RngCore as _, TryRngCore as _};
36use serde::de::DeserializeOwned;
37use serde::{Deserialize, Serialize};
38use sha2::Sha512;
39use uuid::Uuid;
40
41include!(concat!(env!("OUT_DIR"), "/constants.gen.rs"));
42
43const LATEST_VERSION: RelayVersion = RelayVersion::new(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
45
46const OLDEST_VERSION: RelayVersion = RelayVersion::new(0, 0, 0); const MAX_TIME_IN_FUTURE: Duration = Duration::seconds(15);
54
55pub type RelayId = Uuid;
57
58#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
60pub struct RelayVersion {
61 major: u8,
62 minor: u8,
63 patch: u8,
64}
65
66impl RelayVersion {
67 pub fn current() -> Self {
69 LATEST_VERSION
70 }
71
72 pub fn oldest() -> Self {
77 OLDEST_VERSION
78 }
79
80 pub const fn new(major: u8, minor: u8, patch: u8) -> Self {
82 Self {
83 major,
84 minor,
85 patch,
86 }
87 }
88
89 pub fn supported(self) -> bool {
91 self >= Self::oldest()
92 }
93
94 pub fn outdated(self) -> bool {
96 self < Self::current()
97 }
98}
99
100impl fmt::Display for RelayVersion {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
103 }
104}
105
106#[derive(Clone, Copy, Debug, Default, thiserror::Error)]
108#[error("invalid relay version string")]
109pub struct ParseRelayVersionError;
110
111impl FromStr for RelayVersion {
112 type Err = ParseRelayVersionError;
113
114 fn from_str(s: &str) -> Result<Self, Self::Err> {
115 let mut iter = s
116 .split(&['.', '-'][..])
117 .map(|s| s.parse().map_err(|_| ParseRelayVersionError));
118
119 let major = iter.next().ok_or(ParseRelayVersionError)??;
120 let minor = iter.next().ok_or(ParseRelayVersionError)??;
121 let patch = iter.next().ok_or(ParseRelayVersionError)??;
122
123 Ok(Self::new(major, minor, patch))
124 }
125}
126
127relay_common::impl_str_serde!(RelayVersion, "a version string");
128
129#[derive(Debug, Eq, Hash, PartialEq, thiserror::Error)]
131pub enum KeyParseError {
132 #[error("bad key encoding")]
134 BadEncoding,
135 #[error("bad key data")]
137 BadKey,
138}
139
140#[derive(Debug, thiserror::Error, PartialEq, Eq)]
142pub enum TimeError {
143 #[error("the timestamp is too far in the future")]
145 TooFarInFuture,
146 #[error("the timestamp is too far in the past")]
148 TooFarInPast,
149}
150
151#[derive(Debug, thiserror::Error, PartialEq, Eq)]
153pub enum SignatureError {
154 #[error("invalid signature")]
156 Invalid,
157 #[error("signature cannot be verified")]
159 Unverifiable,
160 #[error("{0}")]
162 Time(#[from] TimeError),
163}
164
165#[derive(Debug, thiserror::Error)]
167pub enum UnpackError {
168 #[error("invalid signature on data")]
170 BadSignature,
171 #[error("bad key encoding")]
173 BadEncoding,
174 #[error("could not deserialize payload")]
176 BadPayload(#[source] serde_json::Error),
177 #[error("{0}")]
179 Time(#[from] TimeError),
180}
181
182impl From<SignatureError> for UnpackError {
183 fn from(value: SignatureError) -> Self {
184 match value {
185 SignatureError::Invalid | SignatureError::Unverifiable => Self::BadSignature,
186 SignatureError::Time(time) => Self::Time(time),
187 }
188 }
189}
190
191#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
193pub enum SignatureAlgorithm {
194 #[serde(rename = "v0")]
196 Regular,
197 #[serde(rename = "v1")]
199 Prehashed,
200}
201
202#[derive(Serialize, Deserialize, Debug)]
206pub struct SignatureHeader {
207 #[serde(rename = "t")]
209 pub timestamp: DateTime<Utc>,
210
211 #[serde(rename = "a", skip_serializing_if = "Option::is_none")]
216 pub signature_algorithm: Option<SignatureAlgorithm>,
217}
218
219impl Default for SignatureHeader {
220 fn default() -> SignatureHeader {
221 SignatureHeader {
222 timestamp: Utc::now(),
223 signature_algorithm: None,
224 }
225 }
226}
227
228#[derive(Debug)]
230pub struct VerifiedSignatureHeader {
231 timestamp: DateTime<Utc>,
232 signature_algorithm: SignatureAlgorithm,
233}
234
235impl VerifiedSignatureHeader {
236 pub fn timestamp(&self) -> DateTime<Utc> {
238 self.timestamp
239 }
240
241 pub fn signature_algorithm(&self) -> SignatureAlgorithm {
243 self.signature_algorithm
244 }
245}
246
247#[derive(Clone)]
253pub struct SecretKey {
254 inner: ed25519_dalek::SigningKey,
255}
256
257#[derive(Serialize, Deserialize, Debug)]
259pub struct Registration {
260 relay_id: RelayId,
261}
262
263fn create_digest(header: &[u8], data: &[u8]) -> Sha512 {
265 let mut digest = Sha512::default();
266 digest.update(header);
267 digest.update(b"\x00");
268 digest.update(data);
269 digest
270}
271
272impl SecretKey {
273 pub fn sign(&self, data: &[u8]) -> Signature {
277 self.sign_with_header(data, &SignatureHeader::default())
278 }
279
280 pub fn sign_with_header(&self, data: &[u8], sig_header: &SignatureHeader) -> Signature {
286 let mut header =
287 serde_json::to_vec(&sig_header).expect("attempted to pack non json safe header");
288 let header_encoded = BASE64URL_NOPAD.encode(&header);
289 let sig = match sig_header
290 .signature_algorithm
291 .unwrap_or(SignatureAlgorithm::Regular)
292 {
293 SignatureAlgorithm::Regular => {
294 header.push(b'\x00');
295 header.extend_from_slice(data);
296 self.inner.sign(&header)
297 }
298 SignatureAlgorithm::Prehashed => {
299 let digest = create_digest(&header, data);
300 self.inner.sign_digest(digest)
301 }
302 };
303
304 let mut sig_encoded = BASE64URL_NOPAD.encode(&sig.to_bytes());
305 sig_encoded.push('.');
306 sig_encoded.push_str(&header_encoded);
307 Signature(sig_encoded)
308 }
309
310 pub fn pack<S: Serialize>(&self, data: S) -> (Vec<u8>, Signature) {
312 self.pack_with_header(data, &SignatureHeader::default())
313 }
314
315 pub fn pack_with_header<S: Serialize>(
317 &self,
318 data: S,
319 header: &SignatureHeader,
320 ) -> (Vec<u8>, Signature) {
321 let json = serde_json::to_vec(&data).expect("attempted to pack non json safe data");
324 let sig = self.sign_with_header(&json, header);
325 (json, sig)
326 }
327}
328
329impl PartialEq for SecretKey {
330 fn eq(&self, other: &SecretKey) -> bool {
331 self.inner.to_keypair_bytes() == other.inner.to_keypair_bytes()
332 }
333}
334
335impl Eq for SecretKey {}
336
337impl FromStr for SecretKey {
338 type Err = KeyParseError;
339
340 fn from_str(s: &str) -> Result<SecretKey, KeyParseError> {
341 if let Ok(inner) = ed25519_dalek::SigningKey::from_pkcs8_pem(s) {
342 return Ok(Self { inner });
343 }
344
345 let bytes = match BASE64URL_NOPAD.decode(s.as_bytes()) {
346 Ok(bytes) => bytes,
347 _ => return Err(KeyParseError::BadEncoding),
348 };
349
350 let inner = if let Ok(keypair) = bytes.as_slice().try_into() {
351 ed25519_dalek::SigningKey::from_keypair_bytes(&keypair)
352 .map_err(|_| KeyParseError::BadKey)?
353 } else if let Ok(secret_key) = bytes.try_into() {
354 ed25519_dalek::SigningKey::from_bytes(&secret_key)
355 } else {
356 return Err(KeyParseError::BadKey);
357 };
358
359 Ok(SecretKey { inner })
360 }
361}
362
363impl fmt::Display for SecretKey {
364 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365 if f.alternate() {
366 write!(
367 f,
368 "{}",
369 BASE64URL_NOPAD.encode(&self.inner.to_keypair_bytes())
370 )
371 } else {
372 write!(f, "{}", BASE64URL_NOPAD.encode(&self.inner.to_bytes()))
373 }
374 }
375}
376
377impl fmt::Debug for SecretKey {
378 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
379 write!(f, "SecretKey(\"{self}\")")
380 }
381}
382
383relay_common::impl_str_serde!(SecretKey, "a secret key");
384
385#[derive(Clone, Eq, PartialEq)]
391pub struct PublicKey {
392 inner: ed25519_dalek::VerifyingKey,
393}
394
395impl PublicKey {
396 pub fn verify(
400 &self,
401 data: &[u8],
402 sig: SignatureRef<'_>,
403 start_time: DateTime<Utc>,
404 max_age: Duration,
405 ) -> Result<VerifiedSignatureHeader, SignatureError> {
406 let mut iter = sig.0.splitn(2, '.');
407 let sig_bytes = {
408 let sig_encoded = iter.next().ok_or(SignatureError::Invalid)?;
409 BASE64URL_NOPAD
410 .decode(sig_encoded.as_bytes())
411 .map_err(|_| SignatureError::Invalid)?
412 };
413 let sig = ed25519_dalek::Signature::from_slice(&sig_bytes)
414 .map_err(|_| SignatureError::Invalid)?;
415
416 let header = {
417 let header_encoded = iter.next().ok_or(SignatureError::Invalid)?;
418 BASE64URL_NOPAD
419 .decode(header_encoded.as_bytes())
420 .map_err(|_| SignatureError::Invalid)?
421 };
422 let parsed: SignatureHeader =
423 serde_json::from_slice(&header).map_err(|_| SignatureError::Invalid)?;
424
425 let signature_algorithm = parsed
426 .signature_algorithm
427 .unwrap_or(SignatureAlgorithm::Regular);
429
430 let verification_result = match signature_algorithm {
431 SignatureAlgorithm::Regular => {
432 let mut to_verify = header.clone();
433 to_verify.push(b'\x00');
434 to_verify.extend_from_slice(data);
435 self.inner.verify(&to_verify, &sig)
436 }
437 SignatureAlgorithm::Prehashed => {
438 let digest = create_digest(&header, data);
439 self.inner.verify_digest(digest, &sig)
440 }
441 };
442
443 let Ok(()) = verification_result else {
444 return Err(SignatureError::Unverifiable);
445 };
446
447 let timestamp = verify_time(parsed.timestamp, start_time, max_age)?;
448
449 Ok(VerifiedSignatureHeader {
450 timestamp,
451 signature_algorithm,
452 })
453 }
454
455 pub fn unpack<D: DeserializeOwned>(
457 &self,
458 data: &[u8],
459 signature: SignatureRef<'_>,
460 start_time: DateTime<Utc>,
461 max_age_diff: Duration,
462 ) -> Result<D, UnpackError> {
463 let _verified = self.verify(data, signature, start_time, max_age_diff)?;
464 serde_json::from_slice(data).map_err(UnpackError::BadPayload)
465 }
466}
467
468impl FromStr for PublicKey {
469 type Err = KeyParseError;
470
471 fn from_str(s: &str) -> Result<PublicKey, KeyParseError> {
472 if let Ok(inner) = ed25519_dalek::VerifyingKey::from_public_key_pem(s) {
473 return Ok(Self { inner });
474 }
475
476 let Ok(bytes) = BASE64URL_NOPAD.decode(s.as_bytes()) else {
477 return Err(KeyParseError::BadEncoding);
478 };
479
480 let inner = match bytes.try_into() {
481 Ok(bytes) => ed25519_dalek::VerifyingKey::from_bytes(&bytes)
482 .map_err(|_| KeyParseError::BadKey)?,
483 Err(_) => return Err(KeyParseError::BadKey),
484 };
485
486 Ok(PublicKey { inner })
487 }
488}
489
490impl fmt::Display for PublicKey {
491 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
492 write!(f, "{}", BASE64URL_NOPAD.encode(&self.inner.to_bytes()))
493 }
494}
495
496impl fmt::Debug for PublicKey {
497 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
498 write!(f, "PublicKey(\"{self}\")")
499 }
500}
501
502relay_common::impl_str_serde!(PublicKey, "a public key");
503
504pub fn generate_relay_id() -> RelayId {
506 Uuid::new_v4()
507}
508
509pub fn generate_key_pair() -> (SecretKey, PublicKey) {
511 let mut csprng = OsRng;
512 let mut secret = [0; 32];
513 csprng
514 .try_fill_bytes(&mut secret)
515 .expect("os rng should be available");
516 let kp = ed25519_dalek::SigningKey::from_bytes(&secret);
517 let pk = kp.verifying_key();
518 (SecretKey { inner: kp }, PublicKey { inner: pk })
519}
520
521#[derive(Clone, Debug, Deserialize, Serialize)]
540pub struct SignedRegisterState(String);
541
542impl SignedRegisterState {
543 fn mac(secret: &[u8]) -> Hmac<Sha512> {
545 Hmac::new_from_slice(secret).expect("HMAC takes variable keys")
546 }
547
548 fn sign(state: RegisterState, secret: &[u8]) -> Self {
550 let json = serde_json::to_string(&state).expect("relay register state serializes to JSON");
551 let token = BASE64URL_NOPAD.encode(json.as_bytes());
552
553 let mut mac = Self::mac(secret);
554 mac.update(token.as_bytes());
555 let signature = BASE64URL_NOPAD.encode(&mac.finalize().into_bytes());
556
557 Self(format!("{token}:{signature}"))
558 }
559
560 fn split(&self) -> (&str, &str) {
562 let mut split = self.as_str().splitn(2, ':');
563 (split.next().unwrap_or(""), split.next().unwrap_or(""))
564 }
565
566 pub fn as_str(&self) -> &str {
568 self.0.as_str()
569 }
570
571 pub fn unpack(
575 &self,
576 secret: &[u8],
577 start_time: DateTime<Utc>,
578 max_age: Duration,
579 ) -> Result<RegisterState, UnpackError> {
580 let (token, signature) = self.split();
581 let code = BASE64URL_NOPAD
582 .decode(signature.as_bytes())
583 .map_err(|_| UnpackError::BadEncoding)?;
584
585 let mut mac = Self::mac(secret);
586 mac.update(token.as_bytes());
587 mac.verify_slice(&code)
588 .map_err(|_| UnpackError::BadSignature)?;
589
590 let json = BASE64URL_NOPAD
591 .decode(token.as_bytes())
592 .map_err(|_| UnpackError::BadEncoding)?;
593 let state =
594 serde_json::from_slice::<RegisterState>(&json).map_err(UnpackError::BadPayload)?;
595
596 let _ = verify_time(state.timestamp, start_time, max_age)?;
597
598 Ok(state)
599 }
600}
601
602impl fmt::Display for SignedRegisterState {
603 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
604 self.as_str().fmt(f)
605 }
606}
607
608#[derive(Clone, Deserialize, Serialize)]
614pub struct RegisterState {
615 #[serde(with = "chrono::serde::ts_seconds")]
616 timestamp: DateTime<Utc>,
617 relay_id: RelayId,
618 public_key: PublicKey,
619 rand: String,
620}
621
622impl RegisterState {
623 pub fn timestamp(&self) -> DateTime<Utc> {
625 self.timestamp
626 }
627
628 pub fn relay_id(&self) -> RelayId {
630 self.relay_id
631 }
632
633 pub fn public_key(&self) -> &PublicKey {
635 &self.public_key
636 }
637}
638
639fn nonce() -> String {
641 let mut rng = rand::rng();
642 let mut bytes = vec![0u8; 64];
643 rng.fill_bytes(&mut bytes);
644 BASE64URL_NOPAD.encode(&bytes)
645}
646
647#[derive(Serialize, Deserialize, Debug)]
653pub struct RegisterRequest {
654 relay_id: RelayId,
655 public_key: PublicKey,
656 #[serde(default)]
657 version: RelayVersion,
658}
659
660impl RegisterRequest {
661 pub fn new(relay_id: &RelayId, public_key: &PublicKey) -> RegisterRequest {
663 RegisterRequest {
664 relay_id: *relay_id,
665 public_key: public_key.clone(),
666 version: RelayVersion::current(),
667 }
668 }
669
670 pub fn bootstrap_unpack(
676 data: &[u8],
677 signature: SignatureRef<'_>,
678 start_time: DateTime<Utc>,
679 max_age: Duration,
680 ) -> Result<RegisterRequest, UnpackError> {
681 let req: RegisterRequest = serde_json::from_slice(data).map_err(UnpackError::BadPayload)?;
682 let pk = req.public_key();
683 pk.unpack(data, signature, start_time, max_age)
684 }
685
686 pub fn relay_id(&self) -> RelayId {
688 self.relay_id
689 }
690
691 pub fn public_key(&self) -> &PublicKey {
693 &self.public_key
694 }
695
696 pub fn into_challenge(self, secret: &[u8]) -> RegisterChallenge {
698 let state = RegisterState {
699 timestamp: Utc::now(),
700 relay_id: self.relay_id,
701 public_key: self.public_key,
702 rand: nonce(),
703 };
704
705 RegisterChallenge {
706 relay_id: self.relay_id,
707 token: SignedRegisterState::sign(state, secret),
708 }
709 }
710}
711
712#[derive(Serialize, Deserialize, Debug)]
714pub struct RegisterChallenge {
715 relay_id: RelayId,
716 token: SignedRegisterState,
717}
718
719impl RegisterChallenge {
720 pub fn relay_id(&self) -> &RelayId {
722 &self.relay_id
723 }
724
725 pub fn token(&self) -> &str {
727 self.token.as_str()
728 }
729
730 pub fn into_response(self) -> RegisterResponse {
732 RegisterResponse {
733 relay_id: self.relay_id,
734 token: self.token,
735 version: RelayVersion::current(),
736 }
737 }
738}
739
740#[derive(Serialize, Deserialize, Debug)]
745pub struct RegisterResponse {
746 relay_id: RelayId,
747 token: SignedRegisterState,
748 #[serde(default)]
749 version: RelayVersion,
750}
751
752impl RegisterResponse {
753 pub fn unpack(
755 data: &[u8],
756 signature: SignatureRef<'_>,
757 secret: &[u8],
758 start_time: DateTime<Utc>,
759 max_age: Duration,
760 ) -> Result<(Self, RegisterState), UnpackError> {
761 let response: Self = serde_json::from_slice(data).map_err(UnpackError::BadPayload)?;
762 let state = response.token.unpack(secret, start_time, max_age)?;
763
764 let _verified = state
765 .public_key()
766 .verify(data, signature, start_time, max_age)?;
767
768 Ok((response, state))
769 }
770
771 pub fn relay_id(&self) -> RelayId {
773 self.relay_id
774 }
775
776 pub fn token(&self) -> &str {
778 self.token.as_str()
779 }
780
781 pub fn version(&self) -> RelayVersion {
783 self.version
784 }
785}
786
787#[derive(Debug, Clone, PartialEq)]
789pub struct Signature(pub String);
790
791impl Display for Signature {
792 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
793 write!(f, "{}", self.0)
794 }
795}
796
797impl Signature {
798 pub fn verify_any<'a>(
804 &self,
805 public_key: &'a [PublicKey],
806 start_time: DateTime<Utc>,
807 max_age: Duration,
808 ) -> Option<(&'a PublicKey, VerifiedSignatureHeader)> {
809 public_key.iter().find_map(|p| {
810 let verified = self.verify(&[], p, start_time, max_age).ok()?;
811 Some((p, verified))
812 })
813 }
814
815 pub fn verify(
821 &self,
822 data: &[u8],
823 public_key: &PublicKey,
824 start_time: DateTime<Utc>,
825 max_age_diff: Duration,
826 ) -> Result<VerifiedSignatureHeader, SignatureError> {
827 public_key.verify(data, self.as_signature_ref(), start_time, max_age_diff)
828 }
829
830 pub fn as_signature_ref(&self) -> SignatureRef<'_> {
835 SignatureRef(self.0.as_str())
836 }
837}
838
839pub struct SignatureRef<'a>(pub &'a str);
845
846fn verify_time(
848 ts: DateTime<Utc>,
849 start_time: DateTime<Utc>,
850 max_age: Duration,
851) -> Result<DateTime<Utc>, TimeError> {
852 let diff = start_time - ts;
853 if diff > max_age {
854 Err(TimeError::TooFarInPast)
855 } else if diff < -MAX_TIME_IN_FUTURE {
856 Err(TimeError::TooFarInFuture)
857 } else {
858 Ok(ts)
859 }
860}
861
862#[cfg(test)]
863mod tests {
864 use super::*;
865
866 #[test]
867 fn test_keys() {
868 let sk: SecretKey =
869 "OvXFVm1tIUi8xDTuyHX1SSqdMc8nCt2qU9IUaH5p7oUk5pHZsdnfXNiMWiMLtSE86J3N9Peo5CBP1YQHDUkApQ"
870 .parse()
871 .unwrap();
872 let pk: PublicKey = "JOaR2bHZ31zYjFojC7UhPOidzfT3qOQgT9WEBw1JAKU"
873 .parse()
874 .unwrap();
875
876 assert_eq!(
877 sk.to_string(),
878 "OvXFVm1tIUi8xDTuyHX1SSqdMc8nCt2qU9IUaH5p7oU"
879 );
880 assert_eq!(
881 format!("{sk:#}"),
882 "OvXFVm1tIUi8xDTuyHX1SSqdMc8nCt2qU9IUaH5p7oUk5pHZsdnfXNiMWiMLtSE86J3N9Peo5CBP1YQHDUkApQ"
883 );
884 assert_eq!(
885 pk.to_string(),
886 "JOaR2bHZ31zYjFojC7UhPOidzfT3qOQgT9WEBw1JAKU"
887 );
888
889 assert_eq!(
890 "bad data".parse::<SecretKey>(),
891 Err(KeyParseError::BadEncoding)
892 );
893 assert_eq!("OvXF".parse::<SecretKey>(), Err(KeyParseError::BadKey));
894
895 assert_eq!(
896 "bad data".parse::<PublicKey>(),
897 Err(KeyParseError::BadEncoding)
898 );
899 assert_eq!("OvXF".parse::<PublicKey>(), Err(KeyParseError::BadKey));
900 }
901
902 #[test]
903 fn test_serializing() {
904 let sk: SecretKey =
905 "OvXFVm1tIUi8xDTuyHX1SSqdMc8nCt2qU9IUaH5p7oUk5pHZsdnfXNiMWiMLtSE86J3N9Peo5CBP1YQHDUkApQ"
906 .parse()
907 .unwrap();
908 let pk: PublicKey = "JOaR2bHZ31zYjFojC7UhPOidzfT3qOQgT9WEBw1JAKU"
909 .parse()
910 .unwrap();
911
912 let sk_json = serde_json::to_string(&sk).unwrap();
913 assert_eq!(sk_json, "\"OvXFVm1tIUi8xDTuyHX1SSqdMc8nCt2qU9IUaH5p7oU\"");
914
915 let pk_json = serde_json::to_string(&pk).unwrap();
916 assert_eq!(pk_json, "\"JOaR2bHZ31zYjFojC7UhPOidzfT3qOQgT9WEBw1JAKU\"");
917
918 assert_eq!(serde_json::from_str::<SecretKey>(&sk_json).unwrap(), sk);
919 assert_eq!(serde_json::from_str::<PublicKey>(&pk_json).unwrap(), pk);
920 }
921
922 #[test]
923 fn test_signatures() {
924 let (sk, pk) = generate_key_pair();
925 let data = b"Hello World!";
926
927 let sig = sk.sign(data);
928 let _verified = pk.verify(
929 data,
930 sig.as_signature_ref(),
931 Utc::now(),
932 Duration::seconds(1),
933 );
934
935 let bad_sig = "jgubwSf2wb2wuiRpgt2H9_bdDSMr88hXLp5zVuhbr65EGkSxOfT5ILIWr623twLgLd0bDgHg6xzOaUCX7XvUCw";
936 assert_eq!(
937 pk.verify(data, SignatureRef(bad_sig), Utc::now(), Duration::MAX)
938 .unwrap_err(),
939 SignatureError::Invalid
940 );
941 }
942
943 #[test]
944 fn test_registration() {
945 let max_age = Duration::minutes(15);
946
947 let relay_id = generate_relay_id();
949 let (sk, pk) = generate_key_pair();
950
951 let request = RegisterRequest::new(&relay_id, &pk);
953
954 let (request_bytes, request_sig) = sk.pack(request);
956
957 let request = RegisterRequest::bootstrap_unpack(
959 &request_bytes,
960 request_sig.as_signature_ref(),
961 Utc::now(),
962 max_age,
963 )
964 .unwrap();
965 assert_eq!(request.relay_id(), relay_id);
966 assert_eq!(request.public_key(), &pk);
967
968 let upstream_secret = b"secret";
969
970 let challenge = request.into_challenge(upstream_secret);
972 let challenge_token = challenge.token().to_owned();
973 assert_eq!(challenge.relay_id(), &relay_id);
974 assert!(challenge.token().len() > 40);
975
976 let state = SignedRegisterState(challenge_token.clone());
978 let register_state = state.unpack(upstream_secret, Utc::now(), max_age).unwrap();
979 assert_eq!(register_state.public_key, pk);
980 assert_eq!(register_state.relay_id, relay_id);
981
982 let response = challenge.into_response();
984
985 let (response_bytes, response_sig) = sk.pack(response);
987 let (response, _) = RegisterResponse::unpack(
988 &response_bytes,
989 response_sig.as_signature_ref(),
990 upstream_secret,
991 Utc::now(),
992 max_age,
993 )
994 .unwrap();
995
996 assert_eq!(response.relay_id(), relay_id);
997 assert_eq!(response.token(), challenge_token);
998 assert_eq!(response.version, LATEST_VERSION);
999 }
1000
1001 #[test]
1006 #[allow(clippy::print_stdout, reason = "helper test to generate output")]
1007 fn test_generate_strings_for_test_auth_py() {
1008 let max_age = Duration::minutes(15);
1009 println!("Generating test data for test_auth.py...");
1010
1011 let relay_id = generate_relay_id();
1013 println!("RELAY_ID = b\"{relay_id}\"");
1014 let (sk, pk) = generate_key_pair();
1015 println!("RELAY_KEY = b\"{pk}\"");
1016
1017 let request = RegisterRequest::new(&relay_id, &pk);
1019 println!("REQUEST = b'{}'", serde_json::to_string(&request).unwrap());
1020
1021 let (request_bytes, request_sig) = sk.pack(&request);
1023 println!("REQUEST_SIG = \"{request_sig}\"");
1024
1025 let request = RegisterRequest::bootstrap_unpack(
1027 &request_bytes,
1028 request_sig.as_signature_ref(),
1029 Utc::now(),
1030 max_age,
1031 )
1032 .unwrap();
1033
1034 let upstream_secret = b"secret";
1035
1036 let challenge = request.into_challenge(upstream_secret);
1038 let challenge_token = challenge.token().to_owned();
1039 println!("TOKEN = \"{challenge_token}\"");
1040
1041 let response = challenge.into_response();
1043 let serialized_response = serde_json::to_string(&response).unwrap();
1044 let (_, response_sig) = sk.pack(&response);
1045
1046 println!("RESPONSE = b'{serialized_response}'");
1047 println!("RESPONSE_SIG = \"{response_sig}\"");
1048
1049 println!("RELAY_VERSION = \"{LATEST_VERSION}\"");
1050 }
1051
1052 #[test]
1054 fn test_deserialize_old_response() {
1055 let serialized_challenge = "{\"relay_id\":\"6b7d15b8-cee2-4354-9fee-dae7ef43e434\",\"token\":\"eyJ0aW1lc3RhbXAiOjE1OTg5Njc0MzQsInJlbGF5X2lkIjoiNmI3ZDE1YjgtY2VlMi00MzU0LTlmZWUtZGFlN2VmNDNlNDM0IiwicHVibGljX2tleSI6ImtNcEdieWRIWlN2b2h6ZU1sZ2hjV3dIZDhNa3JlS0d6bF9uY2RrWlNPTWciLCJyYW5kIjoiLUViNG9Hal80dUZYOUNRRzFBVmdqTjRmdGxaNU9DSFlNOFl2d1podmlyVXhUY0tFSWYtQzhHaldsZmgwQTNlMzYxWE01dVh0RHhvN00tbWhZeXpWUWcifQ:KJUDXlwvibKNQmex-_Cu1U0FArlmoDkyqP7bYIDGrLXudfjGfCjH-UjNsUHWVDnbM28YdQ-R2MBSyF51aRLQcw\"}";
1056 let result: RegisterResponse = serde_json::from_str(serialized_challenge).unwrap();
1057 assert_eq!(
1058 result.relay_id,
1059 Uuid::parse_str("6b7d15b8-cee2-4354-9fee-dae7ef43e434").unwrap()
1060 )
1061 }
1062
1063 #[test]
1064 fn test_relay_version_current() {
1065 assert_eq!(
1066 env!("CARGO_PKG_VERSION"),
1067 RelayVersion::current().to_string()
1068 );
1069 }
1070
1071 #[test]
1072 fn test_relay_version_oldest() {
1073 assert_eq!("0.0.0", RelayVersion::oldest().to_string());
1075 }
1076
1077 #[test]
1078 fn test_relay_version_parse() {
1079 assert_eq!(
1080 RelayVersion::new(20, 7, 0),
1081 "20.7.0-beta.0".parse().unwrap()
1082 );
1083 }
1084
1085 #[test]
1086 fn test_relay_version_oldest_supported() {
1087 assert!(RelayVersion::oldest().supported());
1088 }
1089
1090 #[test]
1091 fn test_relay_version_any_supported() {
1092 assert!(RelayVersion::default().supported());
1095 }
1096
1097 #[test]
1098 fn test_relay_version_from_str() {
1099 assert_eq!(RelayVersion::new(20, 7, 0), "20.7.0".parse().unwrap());
1100 }
1101
1102 #[test]
1103 fn test_verify_any() {
1104 let (_, p1) = generate_key_pair();
1105 let (_, p2) = generate_key_pair();
1106 let (s3, p3) = generate_key_pair();
1107
1108 let keys = [p1, p2, p3];
1109 let signature = s3.sign(&[]);
1110
1111 let verification = signature
1112 .verify_any(&keys, Utc::now(), Duration::seconds(10))
1113 .unwrap();
1114 assert_eq!(verification.0, &keys[2]);
1115 }
1116
1117 #[test]
1118 fn test_verify_max_age() {
1119 let pair = generate_key_pair();
1120 let start_time = Utc::now();
1121 let header = SignatureHeader {
1122 timestamp: start_time,
1123 signature_algorithm: None,
1124 };
1125 let signature = pair.0.sign_with_header(&[], &header);
1126
1127 let verified = signature
1129 .verify(&[], &pair.1, start_time, Duration::seconds(10))
1130 .unwrap();
1131 assert_eq!(verified.timestamp(), start_time);
1132
1133 let verified = signature
1135 .verify(
1136 &[],
1137 &pair.1,
1138 start_time - MAX_TIME_IN_FUTURE,
1139 Duration::seconds(10),
1140 )
1141 .unwrap();
1142 assert_eq!(verified.timestamp(), start_time);
1143
1144 let err = signature
1146 .verify(
1147 &[],
1148 &pair.1,
1149 start_time - MAX_TIME_IN_FUTURE - Duration::milliseconds(1),
1150 Duration::seconds(10),
1151 )
1152 .unwrap_err();
1153 assert_eq!(err, SignatureError::Time(TimeError::TooFarInFuture));
1154
1155 let err = signature
1157 .verify(
1158 &[],
1159 &pair.1,
1160 start_time + Duration::seconds(1),
1161 Duration::milliseconds(500),
1162 )
1163 .unwrap_err();
1164 assert_eq!(err, SignatureError::Time(TimeError::TooFarInPast));
1165 }
1166
1167 #[test]
1168 fn test_verify_any_max_age() {
1169 let start_time = Utc::now();
1170 let pair1 = generate_key_pair();
1171 let pair2 = generate_key_pair();
1172 let pair3 = generate_key_pair();
1173
1174 let header = SignatureHeader {
1175 timestamp: start_time,
1176 signature_algorithm: Some(SignatureAlgorithm::Regular),
1177 };
1178 let signature = pair3.0.sign_with_header(&[], &header);
1179
1180 let public_keys = &[pair1.1, pair2.1, pair3.1];
1181
1182 let v = signature
1184 .verify_any(
1185 public_keys,
1186 start_time + Duration::seconds(1),
1187 Duration::seconds(2),
1188 )
1189 .unwrap();
1190 assert_eq!(v.0, &public_keys[2]);
1191 assert!(
1193 signature
1194 .verify_any(
1195 public_keys,
1196 start_time + Duration::seconds(3),
1197 Duration::seconds(2)
1198 )
1199 .is_none()
1200 );
1201 let v = signature
1203 .verify_any(public_keys, start_time, Duration::MAX)
1204 .unwrap();
1205 assert_eq!(v.0, &public_keys[2]);
1206 }
1207
1208 #[test]
1209 fn test_regular_algorithm() {
1210 let (secret, public) = generate_key_pair();
1211 let signature = secret.sign(&[]);
1212 let _verified = signature
1213 .verify(&[], &public, Utc::now(), Duration::seconds(10))
1214 .unwrap();
1215 }
1216
1217 #[test]
1218 fn test_prehashed_algorithm() {
1219 let (secret, public) = generate_key_pair();
1220 let header = SignatureHeader {
1221 timestamp: Utc::now(),
1222 signature_algorithm: Some(SignatureAlgorithm::Prehashed),
1223 };
1224 let signature = secret.sign_with_header(&[], &header);
1225 let _verified = signature
1226 .verify(&[], &public, Utc::now(), Duration::seconds(10))
1227 .unwrap();
1228 }
1229
1230 #[test]
1231 fn test_legacy_signature_can_be_verified() {
1232 #[derive(Serialize)]
1235 struct TestHeader {
1236 #[serde(rename = "t")]
1237 timestamp: Option<DateTime<Utc>>,
1238 }
1239 let header = serde_json::to_string(&TestHeader {
1240 timestamp: Some(Utc::now()),
1241 })
1242 .unwrap();
1243
1244 let data: &[u8] = &[];
1245 let (secret, public) = generate_key_pair();
1246 let mut to_sign = header.clone().into_bytes();
1247 to_sign.push(b'\x00');
1248 to_sign.extend_from_slice(data);
1249 let sig = secret.inner.sign(to_sign.as_slice());
1250 let mut sig_encoded = BASE64URL_NOPAD.encode(sig.to_bytes().as_slice());
1251 sig_encoded.push('.');
1252 sig_encoded.push_str(BASE64URL_NOPAD.encode(header.as_bytes()).as_str());
1253
1254 let _verified = public
1255 .verify(
1256 data,
1257 SignatureRef(sig_encoded.as_str()),
1258 Utc::now(),
1259 Duration::seconds(3),
1260 )
1261 .unwrap();
1262 }
1263
1264 #[test]
1265 fn test_parse_private_pem() {
1266 let s = r#"-----BEGIN PRIVATE KEY-----
1267MC4CAQAwBQYDK2VwBCIEIPBFGz4q5QW27KNimPqb3dr9/pO4o6XR7QIKE1rxGAIK
1268-----END PRIVATE KEY-----"#;
1269 let key: SecretKey = s.parse().unwrap();
1270 assert_eq!(
1271 key.to_string(),
1272 "8EUbPirlBbbso2KY-pvd2v3-k7ijpdHtAgoTWvEYAgo"
1273 );
1274 }
1275
1276 #[test]
1277 fn test_parse_public_pem() {
1278 let s = r#"-----BEGIN PUBLIC KEY-----
1279MCowBQYDK2VwAyEATQCO/kpf2pyVjQyTuzr2qhi8IBxmBm2apZrUjJALYeA=
1280-----END PUBLIC KEY-----"#;
1281 let key: PublicKey = s.parse().unwrap();
1282 assert_eq!(
1283 key.to_string(),
1284 "TQCO_kpf2pyVjQyTuzr2qhi8IBxmBm2apZrUjJALYeA"
1285 );
1286 }
1287}