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 relay_common::time::UnixTimestamp;
37use serde::de::DeserializeOwned;
38use serde::{Deserialize, Serialize};
39use sha2::Sha512;
40use uuid::Uuid;
41
42include!(concat!(env!("OUT_DIR"), "/constants.gen.rs"));
43
44const LATEST_VERSION: RelayVersion = RelayVersion::new(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
46
47const OLDEST_VERSION: RelayVersion = RelayVersion::new(0, 0, 0); pub type RelayId = Uuid;
52
53#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
55pub struct RelayVersion {
56 major: u8,
57 minor: u8,
58 patch: u8,
59}
60
61impl RelayVersion {
62 pub fn current() -> Self {
64 LATEST_VERSION
65 }
66
67 pub fn oldest() -> Self {
72 OLDEST_VERSION
73 }
74
75 pub const fn new(major: u8, minor: u8, patch: u8) -> Self {
77 Self {
78 major,
79 minor,
80 patch,
81 }
82 }
83
84 pub fn supported(self) -> bool {
86 self >= Self::oldest()
87 }
88
89 pub fn outdated(self) -> bool {
91 self < Self::current()
92 }
93}
94
95impl fmt::Display for RelayVersion {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
98 }
99}
100
101#[derive(Clone, Copy, Debug, Default, thiserror::Error)]
103#[error("invalid relay version string")]
104pub struct ParseRelayVersionError;
105
106impl FromStr for RelayVersion {
107 type Err = ParseRelayVersionError;
108
109 fn from_str(s: &str) -> Result<Self, Self::Err> {
110 let mut iter = s
111 .split(&['.', '-'][..])
112 .map(|s| s.parse().map_err(|_| ParseRelayVersionError));
113
114 let major = iter.next().ok_or(ParseRelayVersionError)??;
115 let minor = iter.next().ok_or(ParseRelayVersionError)??;
116 let patch = iter.next().ok_or(ParseRelayVersionError)??;
117
118 Ok(Self::new(major, minor, patch))
119 }
120}
121
122relay_common::impl_str_serde!(RelayVersion, "a version string");
123
124#[derive(Debug, Eq, Hash, PartialEq, thiserror::Error)]
126pub enum KeyParseError {
127 #[error("bad key encoding")]
129 BadEncoding,
130 #[error("bad key data")]
132 BadKey,
133}
134
135#[derive(Debug, thiserror::Error, PartialEq, Eq)]
137pub enum SignatureError {
138 #[error("invalid signature")]
140 Invalid,
141 #[error("signature cannot be verified")]
143 Unverifiable,
144 #[error("signature is too old")]
146 Expired,
147}
148
149#[derive(Debug, thiserror::Error)]
151pub enum UnpackError {
152 #[error("invalid signature on data")]
154 BadSignature,
155 #[error("bad key encoding")]
157 BadEncoding,
158 #[error("could not deserialize payload")]
160 BadPayload(#[source] serde_json::Error),
161 #[error("signature is too old")]
163 SignatureExpired,
164}
165
166impl From<SignatureError> for UnpackError {
167 fn from(value: SignatureError) -> Self {
168 match value {
169 SignatureError::Invalid | SignatureError::Unverifiable => Self::BadSignature,
170 SignatureError::Expired => Self::SignatureExpired,
171 }
172 }
173}
174
175#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
177pub enum SignatureAlgorithm {
178 #[serde(rename = "v0")]
180 Regular,
181 #[serde(rename = "v1")]
183 Prehashed,
184}
185
186#[derive(Serialize, Deserialize, Debug)]
190pub struct SignatureHeader {
191 #[serde(rename = "t")]
193 pub timestamp: DateTime<Utc>,
194
195 #[serde(rename = "a", skip_serializing_if = "Option::is_none")]
200 pub signature_algorithm: Option<SignatureAlgorithm>,
201}
202
203impl Default for SignatureHeader {
204 fn default() -> SignatureHeader {
205 SignatureHeader {
206 timestamp: Utc::now(),
207 signature_algorithm: None,
208 }
209 }
210}
211
212#[derive(Debug)]
214pub struct VerifiedSignatureHeader {
215 timestamp: DateTime<Utc>,
216 signature_algorithm: SignatureAlgorithm,
217}
218
219impl VerifiedSignatureHeader {
220 pub fn timestamp(&self) -> DateTime<Utc> {
222 self.timestamp
223 }
224
225 pub fn signature_algorithm(&self) -> SignatureAlgorithm {
227 self.signature_algorithm
228 }
229}
230
231#[derive(Clone)]
237pub struct SecretKey {
238 inner: ed25519_dalek::SigningKey,
239}
240
241#[derive(Serialize, Deserialize, Debug)]
243pub struct Registration {
244 relay_id: RelayId,
245}
246
247fn create_digest(header: &[u8], data: &[u8]) -> Sha512 {
249 let mut digest = Sha512::default();
250 digest.update(header);
251 digest.update(b"\x00");
252 digest.update(data);
253 digest
254}
255
256impl SecretKey {
257 pub fn sign(&self, data: &[u8]) -> Signature {
261 self.sign_with_header(data, &SignatureHeader::default())
262 }
263
264 pub fn sign_with_header(&self, data: &[u8], sig_header: &SignatureHeader) -> Signature {
270 let mut header =
271 serde_json::to_vec(&sig_header).expect("attempted to pack non json safe header");
272 let header_encoded = BASE64URL_NOPAD.encode(&header);
273 let sig = match sig_header
274 .signature_algorithm
275 .unwrap_or(SignatureAlgorithm::Regular)
276 {
277 SignatureAlgorithm::Regular => {
278 header.push(b'\x00');
279 header.extend_from_slice(data);
280 self.inner.sign(&header)
281 }
282 SignatureAlgorithm::Prehashed => {
283 let digest = create_digest(&header, data);
284 self.inner.sign_digest(digest)
285 }
286 };
287
288 let mut sig_encoded = BASE64URL_NOPAD.encode(&sig.to_bytes());
289 sig_encoded.push('.');
290 sig_encoded.push_str(&header_encoded);
291 Signature(sig_encoded)
292 }
293
294 pub fn pack<S: Serialize>(&self, data: S) -> (Vec<u8>, Signature) {
296 self.pack_with_header(data, &SignatureHeader::default())
297 }
298
299 pub fn pack_with_header<S: Serialize>(
301 &self,
302 data: S,
303 header: &SignatureHeader,
304 ) -> (Vec<u8>, Signature) {
305 let json = serde_json::to_vec(&data).expect("attempted to pack non json safe data");
308 let sig = self.sign_with_header(&json, header);
309 (json, sig)
310 }
311}
312
313impl PartialEq for SecretKey {
314 fn eq(&self, other: &SecretKey) -> bool {
315 self.inner.to_keypair_bytes() == other.inner.to_keypair_bytes()
316 }
317}
318
319impl Eq for SecretKey {}
320
321impl FromStr for SecretKey {
322 type Err = KeyParseError;
323
324 fn from_str(s: &str) -> Result<SecretKey, KeyParseError> {
325 if let Ok(inner) = ed25519_dalek::SigningKey::from_pkcs8_pem(s) {
326 return Ok(Self { inner });
327 }
328
329 let bytes = match BASE64URL_NOPAD.decode(s.as_bytes()) {
330 Ok(bytes) => bytes,
331 _ => return Err(KeyParseError::BadEncoding),
332 };
333
334 let inner = if let Ok(keypair) = bytes.as_slice().try_into() {
335 ed25519_dalek::SigningKey::from_keypair_bytes(&keypair)
336 .map_err(|_| KeyParseError::BadKey)?
337 } else if let Ok(secret_key) = bytes.try_into() {
338 ed25519_dalek::SigningKey::from_bytes(&secret_key)
339 } else {
340 return Err(KeyParseError::BadKey);
341 };
342
343 Ok(SecretKey { inner })
344 }
345}
346
347impl fmt::Display for SecretKey {
348 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
349 if f.alternate() {
350 write!(
351 f,
352 "{}",
353 BASE64URL_NOPAD.encode(&self.inner.to_keypair_bytes())
354 )
355 } else {
356 write!(f, "{}", BASE64URL_NOPAD.encode(&self.inner.to_bytes()))
357 }
358 }
359}
360
361impl fmt::Debug for SecretKey {
362 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
363 write!(f, "SecretKey(\"{self}\")")
364 }
365}
366
367relay_common::impl_str_serde!(SecretKey, "a secret key");
368
369#[derive(Clone, Eq, PartialEq)]
375pub struct PublicKey {
376 inner: ed25519_dalek::VerifyingKey,
377}
378
379impl PublicKey {
380 pub fn verify(
384 &self,
385 data: &[u8],
386 sig: SignatureRef<'_>,
387 start_time: DateTime<Utc>,
388 max_age: Duration,
389 ) -> Result<VerifiedSignatureHeader, SignatureError> {
390 let mut iter = sig.0.splitn(2, '.');
391 let sig_bytes = {
392 let sig_encoded = iter.next().ok_or(SignatureError::Invalid)?;
393 BASE64URL_NOPAD
394 .decode(sig_encoded.as_bytes())
395 .map_err(|_| SignatureError::Invalid)?
396 };
397 let sig = ed25519_dalek::Signature::from_slice(&sig_bytes)
398 .map_err(|_| SignatureError::Invalid)?;
399
400 let header = {
401 let header_encoded = iter.next().ok_or(SignatureError::Invalid)?;
402 BASE64URL_NOPAD
403 .decode(header_encoded.as_bytes())
404 .map_err(|_| SignatureError::Invalid)?
405 };
406 let parsed: SignatureHeader =
407 serde_json::from_slice(&header).map_err(|_| SignatureError::Invalid)?;
408
409 let signature_algorithm = parsed
410 .signature_algorithm
411 .unwrap_or(SignatureAlgorithm::Regular);
413
414 let verification_result = match signature_algorithm {
415 SignatureAlgorithm::Regular => {
416 let mut to_verify = header.clone();
417 to_verify.push(b'\x00');
418 to_verify.extend_from_slice(data);
419 self.inner.verify(&to_verify, &sig)
420 }
421 SignatureAlgorithm::Prehashed => {
422 let digest = create_digest(&header, data);
423 self.inner.verify_digest(digest, &sig)
424 }
425 };
426
427 let Ok(()) = verification_result else {
428 return Err(SignatureError::Unverifiable);
429 };
430
431 if !is_valid_time(parsed.timestamp, start_time, max_age) {
432 return Err(SignatureError::Expired);
433 }
434
435 Ok(VerifiedSignatureHeader {
436 timestamp: parsed.timestamp,
437 signature_algorithm,
438 })
439 }
440
441 pub fn unpack<D: DeserializeOwned>(
443 &self,
444 data: &[u8],
445 signature: SignatureRef<'_>,
446 start_time: DateTime<Utc>,
447 max_age_diff: Duration,
448 ) -> Result<D, UnpackError> {
449 let _verified = self.verify(data, signature, start_time, max_age_diff)?;
450 serde_json::from_slice(data).map_err(UnpackError::BadPayload)
451 }
452}
453
454impl FromStr for PublicKey {
455 type Err = KeyParseError;
456
457 fn from_str(s: &str) -> Result<PublicKey, KeyParseError> {
458 if let Ok(inner) = ed25519_dalek::VerifyingKey::from_public_key_pem(s) {
459 return Ok(Self { inner });
460 }
461
462 let Ok(bytes) = BASE64URL_NOPAD.decode(s.as_bytes()) else {
463 return Err(KeyParseError::BadEncoding);
464 };
465
466 let inner = match bytes.try_into() {
467 Ok(bytes) => ed25519_dalek::VerifyingKey::from_bytes(&bytes)
468 .map_err(|_| KeyParseError::BadKey)?,
469 Err(_) => return Err(KeyParseError::BadKey),
470 };
471
472 Ok(PublicKey { inner })
473 }
474}
475
476impl fmt::Display for PublicKey {
477 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
478 write!(f, "{}", BASE64URL_NOPAD.encode(&self.inner.to_bytes()))
479 }
480}
481
482impl fmt::Debug for PublicKey {
483 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
484 write!(f, "PublicKey(\"{self}\")")
485 }
486}
487
488relay_common::impl_str_serde!(PublicKey, "a public key");
489
490pub fn generate_relay_id() -> RelayId {
492 Uuid::new_v4()
493}
494
495pub fn generate_key_pair() -> (SecretKey, PublicKey) {
497 let mut csprng = OsRng;
498 let mut secret = [0; 32];
499 csprng
500 .try_fill_bytes(&mut secret)
501 .expect("os rng should be available");
502 let kp = ed25519_dalek::SigningKey::from_bytes(&secret);
503 let pk = kp.verifying_key();
504 (SecretKey { inner: kp }, PublicKey { inner: pk })
505}
506
507#[derive(Clone, Debug, Deserialize, Serialize)]
526pub struct SignedRegisterState(String);
527
528impl SignedRegisterState {
529 fn mac(secret: &[u8]) -> Hmac<Sha512> {
531 Hmac::new_from_slice(secret).expect("HMAC takes variable keys")
532 }
533
534 fn sign(state: RegisterState, secret: &[u8]) -> Self {
536 let json = serde_json::to_string(&state).expect("relay register state serializes to JSON");
537 let token = BASE64URL_NOPAD.encode(json.as_bytes());
538
539 let mut mac = Self::mac(secret);
540 mac.update(token.as_bytes());
541 let signature = BASE64URL_NOPAD.encode(&mac.finalize().into_bytes());
542
543 Self(format!("{token}:{signature}"))
544 }
545
546 fn split(&self) -> (&str, &str) {
548 let mut split = self.as_str().splitn(2, ':');
549 (split.next().unwrap_or(""), split.next().unwrap_or(""))
550 }
551
552 pub fn as_str(&self) -> &str {
554 self.0.as_str()
555 }
556
557 pub fn unpack(
562 &self,
563 secret: &[u8],
564 start_time: DateTime<Utc>,
565 max_age: Duration,
566 ) -> Result<RegisterState, UnpackError> {
567 let (token, signature) = self.split();
568 let code = BASE64URL_NOPAD
569 .decode(signature.as_bytes())
570 .map_err(|_| UnpackError::BadEncoding)?;
571
572 let mut mac = Self::mac(secret);
573 mac.update(token.as_bytes());
574 mac.verify_slice(&code)
575 .map_err(|_| UnpackError::BadSignature)?;
576
577 let json = BASE64URL_NOPAD
578 .decode(token.as_bytes())
579 .map_err(|_| UnpackError::BadEncoding)?;
580 let state =
581 serde_json::from_slice::<RegisterState>(&json).map_err(UnpackError::BadPayload)?;
582
583 let ts = state
584 .timestamp
585 .as_datetime()
586 .ok_or(UnpackError::SignatureExpired)?;
587 if !is_valid_time(ts, start_time, max_age) {
588 return Err(UnpackError::SignatureExpired);
589 }
590
591 Ok(state)
592 }
593}
594
595impl fmt::Display for SignedRegisterState {
596 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
597 self.as_str().fmt(f)
598 }
599}
600
601#[derive(Clone, Deserialize, Serialize)]
607pub struct RegisterState {
608 timestamp: UnixTimestamp,
609 relay_id: RelayId,
610 public_key: PublicKey,
611 rand: String,
612}
613
614impl RegisterState {
615 pub fn timestamp(&self) -> UnixTimestamp {
617 self.timestamp
618 }
619
620 pub fn relay_id(&self) -> RelayId {
622 self.relay_id
623 }
624
625 pub fn public_key(&self) -> &PublicKey {
627 &self.public_key
628 }
629}
630
631fn nonce() -> String {
633 let mut rng = rand::rng();
634 let mut bytes = vec![0u8; 64];
635 rng.fill_bytes(&mut bytes);
636 BASE64URL_NOPAD.encode(&bytes)
637}
638
639#[derive(Serialize, Deserialize, Debug)]
645pub struct RegisterRequest {
646 relay_id: RelayId,
647 public_key: PublicKey,
648 #[serde(default)]
649 version: RelayVersion,
650}
651
652impl RegisterRequest {
653 pub fn new(relay_id: &RelayId, public_key: &PublicKey) -> RegisterRequest {
655 RegisterRequest {
656 relay_id: *relay_id,
657 public_key: public_key.clone(),
658 version: RelayVersion::current(),
659 }
660 }
661
662 pub fn bootstrap_unpack(
668 data: &[u8],
669 signature: SignatureRef<'_>,
670 start_time: DateTime<Utc>,
671 max_age: Duration,
672 ) -> Result<RegisterRequest, UnpackError> {
673 let req: RegisterRequest = serde_json::from_slice(data).map_err(UnpackError::BadPayload)?;
674 let pk = req.public_key();
675 pk.unpack(data, signature, start_time, max_age)
676 }
677
678 pub fn relay_id(&self) -> RelayId {
680 self.relay_id
681 }
682
683 pub fn public_key(&self) -> &PublicKey {
685 &self.public_key
686 }
687
688 pub fn into_challenge(self, secret: &[u8]) -> RegisterChallenge {
690 let state = RegisterState {
691 timestamp: UnixTimestamp::now(),
692 relay_id: self.relay_id,
693 public_key: self.public_key,
694 rand: nonce(),
695 };
696
697 RegisterChallenge {
698 relay_id: self.relay_id,
699 token: SignedRegisterState::sign(state, secret),
700 }
701 }
702}
703
704#[derive(Serialize, Deserialize, Debug)]
706pub struct RegisterChallenge {
707 relay_id: RelayId,
708 token: SignedRegisterState,
709}
710
711impl RegisterChallenge {
712 pub fn relay_id(&self) -> &RelayId {
714 &self.relay_id
715 }
716
717 pub fn token(&self) -> &str {
719 self.token.as_str()
720 }
721
722 pub fn into_response(self) -> RegisterResponse {
724 RegisterResponse {
725 relay_id: self.relay_id,
726 token: self.token,
727 version: RelayVersion::current(),
728 }
729 }
730}
731
732#[derive(Serialize, Deserialize, Debug)]
737pub struct RegisterResponse {
738 relay_id: RelayId,
739 token: SignedRegisterState,
740 #[serde(default)]
741 version: RelayVersion,
742}
743
744impl RegisterResponse {
745 pub fn unpack(
747 data: &[u8],
748 signature: SignatureRef<'_>,
749 secret: &[u8],
750 start_time: DateTime<Utc>,
751 max_age: Duration,
752 ) -> Result<(Self, RegisterState), UnpackError> {
753 let response: Self = serde_json::from_slice(data).map_err(UnpackError::BadPayload)?;
754 let state = response.token.unpack(secret, start_time, max_age)?;
755
756 let _verified = state
757 .public_key()
758 .verify(data, signature, start_time, max_age)?;
759
760 Ok((response, state))
761 }
762
763 pub fn relay_id(&self) -> RelayId {
765 self.relay_id
766 }
767
768 pub fn token(&self) -> &str {
770 self.token.as_str()
771 }
772
773 pub fn version(&self) -> RelayVersion {
775 self.version
776 }
777}
778
779#[derive(Debug, Clone, PartialEq)]
781pub struct Signature(pub String);
782
783impl Display for Signature {
784 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
785 write!(f, "{}", self.0)
786 }
787}
788
789impl Signature {
790 pub fn verify_any<'a>(
796 &self,
797 public_key: &'a [PublicKey],
798 start_time: DateTime<Utc>,
799 max_age: Duration,
800 ) -> Option<(&'a PublicKey, VerifiedSignatureHeader)> {
801 public_key.iter().find_map(|p| {
802 let verified = self.verify(&[], p, start_time, max_age).ok()?;
803 Some((p, verified))
804 })
805 }
806
807 pub fn verify(
813 &self,
814 data: &[u8],
815 public_key: &PublicKey,
816 start_time: DateTime<Utc>,
817 max_age_diff: Duration,
818 ) -> Result<VerifiedSignatureHeader, SignatureError> {
819 public_key.verify(data, self.as_signature_ref(), start_time, max_age_diff)
820 }
821
822 pub fn as_signature_ref(&self) -> SignatureRef<'_> {
827 SignatureRef(self.0.as_str())
828 }
829}
830
831pub struct SignatureRef<'a>(pub &'a str);
837
838fn is_valid_time(ts: DateTime<Utc>, start_time: DateTime<Utc>, max_age: Duration) -> bool {
840 let diff = start_time - ts;
841 diff >= Duration::zero() && diff <= max_age
842}
843
844#[cfg(test)]
845mod tests {
846 use super::*;
847
848 #[test]
849 fn test_keys() {
850 let sk: SecretKey =
851 "OvXFVm1tIUi8xDTuyHX1SSqdMc8nCt2qU9IUaH5p7oUk5pHZsdnfXNiMWiMLtSE86J3N9Peo5CBP1YQHDUkApQ"
852 .parse()
853 .unwrap();
854 let pk: PublicKey = "JOaR2bHZ31zYjFojC7UhPOidzfT3qOQgT9WEBw1JAKU"
855 .parse()
856 .unwrap();
857
858 assert_eq!(
859 sk.to_string(),
860 "OvXFVm1tIUi8xDTuyHX1SSqdMc8nCt2qU9IUaH5p7oU"
861 );
862 assert_eq!(
863 format!("{sk:#}"),
864 "OvXFVm1tIUi8xDTuyHX1SSqdMc8nCt2qU9IUaH5p7oUk5pHZsdnfXNiMWiMLtSE86J3N9Peo5CBP1YQHDUkApQ"
865 );
866 assert_eq!(
867 pk.to_string(),
868 "JOaR2bHZ31zYjFojC7UhPOidzfT3qOQgT9WEBw1JAKU"
869 );
870
871 assert_eq!(
872 "bad data".parse::<SecretKey>(),
873 Err(KeyParseError::BadEncoding)
874 );
875 assert_eq!("OvXF".parse::<SecretKey>(), Err(KeyParseError::BadKey));
876
877 assert_eq!(
878 "bad data".parse::<PublicKey>(),
879 Err(KeyParseError::BadEncoding)
880 );
881 assert_eq!("OvXF".parse::<PublicKey>(), Err(KeyParseError::BadKey));
882 }
883
884 #[test]
885 fn test_serializing() {
886 let sk: SecretKey =
887 "OvXFVm1tIUi8xDTuyHX1SSqdMc8nCt2qU9IUaH5p7oUk5pHZsdnfXNiMWiMLtSE86J3N9Peo5CBP1YQHDUkApQ"
888 .parse()
889 .unwrap();
890 let pk: PublicKey = "JOaR2bHZ31zYjFojC7UhPOidzfT3qOQgT9WEBw1JAKU"
891 .parse()
892 .unwrap();
893
894 let sk_json = serde_json::to_string(&sk).unwrap();
895 assert_eq!(sk_json, "\"OvXFVm1tIUi8xDTuyHX1SSqdMc8nCt2qU9IUaH5p7oU\"");
896
897 let pk_json = serde_json::to_string(&pk).unwrap();
898 assert_eq!(pk_json, "\"JOaR2bHZ31zYjFojC7UhPOidzfT3qOQgT9WEBw1JAKU\"");
899
900 assert_eq!(serde_json::from_str::<SecretKey>(&sk_json).unwrap(), sk);
901 assert_eq!(serde_json::from_str::<PublicKey>(&pk_json).unwrap(), pk);
902 }
903
904 #[test]
905 fn test_signatures() {
906 let (sk, pk) = generate_key_pair();
907 let data = b"Hello World!";
908
909 let sig = sk.sign(data);
910 let _verified = pk.verify(
911 data,
912 sig.as_signature_ref(),
913 Utc::now(),
914 Duration::seconds(1),
915 );
916
917 let bad_sig = "jgubwSf2wb2wuiRpgt2H9_bdDSMr88hXLp5zVuhbr65EGkSxOfT5ILIWr623twLgLd0bDgHg6xzOaUCX7XvUCw";
918 assert_eq!(
919 pk.verify(data, SignatureRef(bad_sig), Utc::now(), Duration::MAX)
920 .unwrap_err(),
921 SignatureError::Invalid
922 );
923 }
924
925 #[test]
926 fn test_registration() {
927 let max_age = Duration::minutes(15);
928
929 let relay_id = generate_relay_id();
931 let (sk, pk) = generate_key_pair();
932
933 let request = RegisterRequest::new(&relay_id, &pk);
935
936 let (request_bytes, request_sig) = sk.pack(request);
938
939 let request = RegisterRequest::bootstrap_unpack(
941 &request_bytes,
942 request_sig.as_signature_ref(),
943 Utc::now(),
944 max_age,
945 )
946 .unwrap();
947 assert_eq!(request.relay_id(), relay_id);
948 assert_eq!(request.public_key(), &pk);
949
950 let upstream_secret = b"secret";
951
952 let challenge = request.into_challenge(upstream_secret);
954 let challenge_token = challenge.token().to_owned();
955 assert_eq!(challenge.relay_id(), &relay_id);
956 assert!(challenge.token().len() > 40);
957
958 let state = SignedRegisterState(challenge_token.clone());
960 let register_state = state.unpack(upstream_secret, Utc::now(), max_age).unwrap();
961 assert_eq!(register_state.public_key, pk);
962 assert_eq!(register_state.relay_id, relay_id);
963
964 let response = challenge.into_response();
966
967 let (response_bytes, response_sig) = sk.pack(response);
969 let (response, _) = RegisterResponse::unpack(
970 &response_bytes,
971 response_sig.as_signature_ref(),
972 upstream_secret,
973 Utc::now(),
974 max_age,
975 )
976 .unwrap();
977
978 assert_eq!(response.relay_id(), relay_id);
979 assert_eq!(response.token(), challenge_token);
980 assert_eq!(response.version, LATEST_VERSION);
981 }
982
983 #[test]
988 #[allow(clippy::print_stdout, reason = "helper test to generate output")]
989 fn test_generate_strings_for_test_auth_py() {
990 let max_age = Duration::minutes(15);
991 println!("Generating test data for test_auth.py...");
992
993 let relay_id = generate_relay_id();
995 println!("RELAY_ID = b\"{relay_id}\"");
996 let (sk, pk) = generate_key_pair();
997 println!("RELAY_KEY = b\"{pk}\"");
998
999 let request = RegisterRequest::new(&relay_id, &pk);
1001 println!("REQUEST = b'{}'", serde_json::to_string(&request).unwrap());
1002
1003 let (request_bytes, request_sig) = sk.pack(&request);
1005 println!("REQUEST_SIG = \"{request_sig}\"");
1006
1007 let request = RegisterRequest::bootstrap_unpack(
1009 &request_bytes,
1010 request_sig.as_signature_ref(),
1011 Utc::now(),
1012 max_age,
1013 )
1014 .unwrap();
1015
1016 let upstream_secret = b"secret";
1017
1018 let challenge = request.into_challenge(upstream_secret);
1020 let challenge_token = challenge.token().to_owned();
1021 println!("TOKEN = \"{challenge_token}\"");
1022
1023 let response = challenge.into_response();
1025 let serialized_response = serde_json::to_string(&response).unwrap();
1026 let (_, response_sig) = sk.pack(&response);
1027
1028 println!("RESPONSE = b'{serialized_response}'");
1029 println!("RESPONSE_SIG = \"{response_sig}\"");
1030
1031 println!("RELAY_VERSION = \"{LATEST_VERSION}\"");
1032 }
1033
1034 #[test]
1036 fn test_deserialize_old_response() {
1037 let serialized_challenge = "{\"relay_id\":\"6b7d15b8-cee2-4354-9fee-dae7ef43e434\",\"token\":\"eyJ0aW1lc3RhbXAiOjE1OTg5Njc0MzQsInJlbGF5X2lkIjoiNmI3ZDE1YjgtY2VlMi00MzU0LTlmZWUtZGFlN2VmNDNlNDM0IiwicHVibGljX2tleSI6ImtNcEdieWRIWlN2b2h6ZU1sZ2hjV3dIZDhNa3JlS0d6bF9uY2RrWlNPTWciLCJyYW5kIjoiLUViNG9Hal80dUZYOUNRRzFBVmdqTjRmdGxaNU9DSFlNOFl2d1podmlyVXhUY0tFSWYtQzhHaldsZmgwQTNlMzYxWE01dVh0RHhvN00tbWhZeXpWUWcifQ:KJUDXlwvibKNQmex-_Cu1U0FArlmoDkyqP7bYIDGrLXudfjGfCjH-UjNsUHWVDnbM28YdQ-R2MBSyF51aRLQcw\"}";
1038 let result: RegisterResponse = serde_json::from_str(serialized_challenge).unwrap();
1039 assert_eq!(
1040 result.relay_id,
1041 Uuid::parse_str("6b7d15b8-cee2-4354-9fee-dae7ef43e434").unwrap()
1042 )
1043 }
1044
1045 #[test]
1046 fn test_relay_version_current() {
1047 assert_eq!(
1048 env!("CARGO_PKG_VERSION"),
1049 RelayVersion::current().to_string()
1050 );
1051 }
1052
1053 #[test]
1054 fn test_relay_version_oldest() {
1055 assert_eq!("0.0.0", RelayVersion::oldest().to_string());
1057 }
1058
1059 #[test]
1060 fn test_relay_version_parse() {
1061 assert_eq!(
1062 RelayVersion::new(20, 7, 0),
1063 "20.7.0-beta.0".parse().unwrap()
1064 );
1065 }
1066
1067 #[test]
1068 fn test_relay_version_oldest_supported() {
1069 assert!(RelayVersion::oldest().supported());
1070 }
1071
1072 #[test]
1073 fn test_relay_version_any_supported() {
1074 assert!(RelayVersion::default().supported());
1077 }
1078
1079 #[test]
1080 fn test_relay_version_from_str() {
1081 assert_eq!(RelayVersion::new(20, 7, 0), "20.7.0".parse().unwrap());
1082 }
1083
1084 #[test]
1085 fn test_verify_any() {
1086 let (_, p1) = generate_key_pair();
1087 let (_, p2) = generate_key_pair();
1088 let (s3, p3) = generate_key_pair();
1089
1090 let keys = [p1, p2, p3];
1091 let signature = s3.sign(&[]);
1092
1093 let verification = signature
1094 .verify_any(&keys, Utc::now(), Duration::seconds(10))
1095 .unwrap();
1096 assert_eq!(verification.0, &keys[2]);
1097 }
1098
1099 #[test]
1100 fn test_verify_max_age() {
1101 let pair = generate_key_pair();
1102 let signature = pair.0.sign(&[]);
1103 let start_time = Utc::now();
1104
1105 let _verified = signature
1107 .verify(&[], &pair.1, start_time, Duration::seconds(10))
1108 .unwrap();
1109
1110 let err = signature
1112 .verify(
1113 &[],
1114 &pair.1,
1115 start_time - Duration::seconds(1),
1116 Duration::milliseconds(500),
1117 )
1118 .unwrap_err();
1119 assert_eq!(err, SignatureError::Expired);
1120
1121 let err = signature
1123 .verify(
1124 &[],
1125 &pair.1,
1126 start_time + Duration::seconds(1),
1127 Duration::milliseconds(500),
1128 )
1129 .unwrap_err();
1130 assert_eq!(err, SignatureError::Expired);
1131 }
1132
1133 #[test]
1134 fn test_verify_any_max_age() {
1135 let start_time = Utc::now();
1136 let pair1 = generate_key_pair();
1137 let pair2 = generate_key_pair();
1138 let pair3 = generate_key_pair();
1139
1140 let header = SignatureHeader {
1141 timestamp: start_time,
1142 signature_algorithm: Some(SignatureAlgorithm::Regular),
1143 };
1144 let signature = pair3.0.sign_with_header(&[], &header);
1145
1146 let public_keys = &[pair1.1, pair2.1, pair3.1];
1147
1148 let v = signature
1150 .verify_any(
1151 public_keys,
1152 start_time + Duration::seconds(1),
1153 Duration::seconds(2),
1154 )
1155 .unwrap();
1156 assert_eq!(v.0, &public_keys[2]);
1157 assert!(
1159 signature
1160 .verify_any(
1161 public_keys,
1162 start_time + Duration::seconds(3),
1163 Duration::seconds(2)
1164 )
1165 .is_none()
1166 );
1167 let v = signature
1169 .verify_any(public_keys, start_time, Duration::MAX)
1170 .unwrap();
1171 assert_eq!(v.0, &public_keys[2]);
1172 }
1173
1174 #[test]
1175 fn test_regular_algorithm() {
1176 let (secret, public) = generate_key_pair();
1177 let signature = secret.sign(&[]);
1178 let _verified = signature
1179 .verify(&[], &public, Utc::now(), Duration::seconds(10))
1180 .unwrap();
1181 }
1182
1183 #[test]
1184 fn test_prehashed_algorithm() {
1185 let (secret, public) = generate_key_pair();
1186 let header = SignatureHeader {
1187 timestamp: Utc::now(),
1188 signature_algorithm: Some(SignatureAlgorithm::Prehashed),
1189 };
1190 let signature = secret.sign_with_header(&[], &header);
1191 let _verified = signature
1192 .verify(&[], &public, Utc::now(), Duration::seconds(10))
1193 .unwrap();
1194 }
1195
1196 #[test]
1197 fn test_legacy_signature_can_be_verified() {
1198 #[derive(Serialize)]
1201 struct TestHeader {
1202 #[serde(rename = "t")]
1203 timestamp: Option<DateTime<Utc>>,
1204 }
1205 let header = serde_json::to_string(&TestHeader {
1206 timestamp: Some(Utc::now()),
1207 })
1208 .unwrap();
1209
1210 let data: &[u8] = &[];
1211 let (secret, public) = generate_key_pair();
1212 let mut to_sign = header.clone().into_bytes();
1213 to_sign.push(b'\x00');
1214 to_sign.extend_from_slice(data);
1215 let sig = secret.inner.sign(to_sign.as_slice());
1216 let mut sig_encoded = BASE64URL_NOPAD.encode(sig.to_bytes().as_slice());
1217 sig_encoded.push('.');
1218 sig_encoded.push_str(BASE64URL_NOPAD.encode(header.as_bytes()).as_str());
1219
1220 let _verified = public
1221 .verify(
1222 data,
1223 SignatureRef(sig_encoded.as_str()),
1224 Utc::now(),
1225 Duration::seconds(3),
1226 )
1227 .unwrap();
1228 }
1229
1230 #[test]
1231 fn test_parse_private_pem() {
1232 let s = r#"-----BEGIN PRIVATE KEY-----
1233MC4CAQAwBQYDK2VwBCIEIPBFGz4q5QW27KNimPqb3dr9/pO4o6XR7QIKE1rxGAIK
1234-----END PRIVATE KEY-----"#;
1235 let key: SecretKey = s.parse().unwrap();
1236 assert_eq!(
1237 key.to_string(),
1238 "8EUbPirlBbbso2KY-pvd2v3-k7ijpdHtAgoTWvEYAgo"
1239 );
1240 }
1241
1242 #[test]
1243 fn test_parse_public_pem() {
1244 let s = r#"-----BEGIN PUBLIC KEY-----
1245MCowBQYDK2VwAyEATQCO/kpf2pyVjQyTuzr2qhi8IBxmBm2apZrUjJALYeA=
1246-----END PUBLIC KEY-----"#;
1247 let key: PublicKey = s.parse().unwrap();
1248 assert_eq!(
1249 key.to_string(),
1250 "TQCO_kpf2pyVjQyTuzr2qhi8IBxmBm2apZrUjJALYeA"
1251 );
1252 }
1253}