Skip to main content

relay_auth/
lib.rs

1//! Authentication and crypto for Relay.
2//!
3//! This library contains the [`PublicKey`] and [`SecretKey`] types, which can be used to validate
4//! and sign traffic between Relays in authenticated endpoints. Additionally, Relays identify via a
5//! [`RelayId`], which is included in the request signature and headers.
6//!
7//! Relay uses Ed25519 at the moment. This is considered an implementation detail and is subject to
8//! change at any time. Do not rely on a specific signing mechanism.
9//!
10//! # Generating Credentials
11//!
12//! Use the [`generate_relay_id`] and [`generate_key_pair`] function to generate credentials:
13//!
14//! ```
15//! let relay_id = relay_auth::generate_relay_id();
16//! let (private_key, public_key) = relay_auth::generate_key_pair();
17//! ```
18
19#![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
43/// The latest Relay version known to this Relay. This is the current version.
44const LATEST_VERSION: RelayVersion = RelayVersion::new(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
45
46/// The oldest downstream Relay version still supported by this Relay.
47const OLDEST_VERSION: RelayVersion = RelayVersion::new(0, 0, 0); // support all
48
49/// The maximum time a timestamp is allowed to be in the future.
50///
51/// Timestamps are always expected to be in the past, but due to time sync inconsistencies
52/// we allow a certain leniency for timestamps to be in the future.
53const MAX_TIME_IN_FUTURE: Duration = Duration::seconds(15);
54
55/// Alias for Relay IDs (UUIDs).
56pub type RelayId = Uuid;
57
58/// The version of a Relay.
59#[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    /// Returns the current Relay version.
68    pub fn current() -> Self {
69        LATEST_VERSION
70    }
71
72    /// Returns the oldest compatible Relay version.
73    ///
74    /// Relays older than this cannot authenticate with this Relay. It is possible for newer Relays
75    /// to authenticate.
76    pub fn oldest() -> Self {
77        OLDEST_VERSION
78    }
79
80    /// Creates a new version with the given components.
81    pub const fn new(major: u8, minor: u8, patch: u8) -> Self {
82        Self {
83            major,
84            minor,
85            patch,
86        }
87    }
88
89    /// Returns `true` if this version is still supported.
90    pub fn supported(self) -> bool {
91        self >= Self::oldest()
92    }
93
94    /// Returns `true` if this version is older than the current version.
95    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/// Raised if Relay cannot parse the provided version.
107#[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/// Raised if a key could not be parsed.
130#[derive(Debug, Eq, Hash, PartialEq, thiserror::Error)]
131pub enum KeyParseError {
132    /// Invalid key encoding.
133    #[error("bad key encoding")]
134    BadEncoding,
135    /// Invalid key data.
136    #[error("bad key data")]
137    BadKey,
138}
139
140/// Raised if the signature timestamp is not valid.
141#[derive(Debug, thiserror::Error, PartialEq, Eq)]
142pub enum TimeError {
143    /// The signature timestamp is too far in the future.
144    #[error("the timestamp is too far in the future")]
145    TooFarInFuture,
146    /// The signature timestamp is too far in the past.
147    #[error("the timestamp is too far in the past")]
148    TooFarInPast,
149}
150
151/// Raised to indicate errors when verifying a signature.
152#[derive(Debug, thiserror::Error, PartialEq, Eq)]
153pub enum SignatureError {
154    /// Raised if the signature is structurally invalid.
155    #[error("invalid signature")]
156    Invalid,
157    /// Raised if the signature is structurally valid but cannot be verified.
158    #[error("signature cannot be verified")]
159    Unverifiable,
160    /// Raised if the signature timestamp cannot be verified.
161    #[error("{0}")]
162    Time(#[from] TimeError),
163}
164
165/// Raised to indicate failure on unpacking.
166#[derive(Debug, thiserror::Error)]
167pub enum UnpackError {
168    /// Raised if the signature is invalid.
169    #[error("invalid signature on data")]
170    BadSignature,
171    /// Invalid key encoding.
172    #[error("bad key encoding")]
173    BadEncoding,
174    /// Raised if deserializing of data failed.
175    #[error("could not deserialize payload")]
176    BadPayload(#[source] serde_json::Error),
177    /// Raised on unpacking if the data carries an invalid timestamp.
178    #[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/// Used to tell which algorithm was used for signature creation.
192#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
193pub enum SignatureAlgorithm {
194    /// Regular signature creation which clones the data internally.
195    #[serde(rename = "v0")]
196    Regular,
197    /// Pre-hashed signature which allows incremental hashing.
198    #[serde(rename = "v1")]
199    Prehashed,
200}
201
202/// A wrapper around packed data that adds a timestamp.
203///
204/// This is internally automatically used when data is signed.
205#[derive(Serialize, Deserialize, Debug)]
206pub struct SignatureHeader {
207    /// The timestamp of when the data was packed and signed.
208    #[serde(rename = "t")]
209    pub timestamp: DateTime<Utc>,
210
211    /// Represents how this signature was created and how it needs to be verified.
212    ///
213    /// Defaults to [`SignatureAlgorithm::Regular`] because that was used before the introduction
214    /// of this field.
215    #[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/// A [`SignatureHeader`] which has been verified.
229#[derive(Debug)]
230pub struct VerifiedSignatureHeader {
231    timestamp: DateTime<Utc>,
232    signature_algorithm: SignatureAlgorithm,
233}
234
235impl VerifiedSignatureHeader {
236    /// Returns the [`SignatureHeader::timestamp`] of the verified header.
237    pub fn timestamp(&self) -> DateTime<Utc> {
238        self.timestamp
239    }
240
241    /// Returns the [`SignatureHeader::signature_algorithm`] of the verified header.
242    pub fn signature_algorithm(&self) -> SignatureAlgorithm {
243        self.signature_algorithm
244    }
245}
246
247/// Represents the secret key of an Relay.
248///
249/// Secret keys are based on ed25519 but this should be considered an
250/// implementation detail for now.  We only ever represent public keys
251/// on the wire as opaque ascii encoded strings of arbitrary format or length.
252#[derive(Clone)]
253pub struct SecretKey {
254    inner: ed25519_dalek::SigningKey,
255}
256
257/// Represents the final registration.
258#[derive(Serialize, Deserialize, Debug)]
259pub struct Registration {
260    relay_id: RelayId,
261}
262
263/// Creates a digest for signature verification/signing.
264fn 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    /// Signs some data with the secret key and returns the signature.
274    ///
275    /// This is will sign with the default header.
276    pub fn sign(&self, data: &[u8]) -> Signature {
277        self.sign_with_header(data, &SignatureHeader::default())
278    }
279
280    /// Signs some data with the secret key and a specific header and
281    /// then returns the signature.
282    ///
283    /// The default behavior is to attach the timestamp in the header to the
284    /// signature so that old signatures on verification can be rejected.
285    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    /// Packs some serializable data into JSON and signs it with the default header.
311    pub fn pack<S: Serialize>(&self, data: S) -> (Vec<u8>, Signature) {
312        self.pack_with_header(data, &SignatureHeader::default())
313    }
314
315    /// Packs some serializable data into JSON and signs it with the specified header.
316    pub fn pack_with_header<S: Serialize>(
317        &self,
318        data: S,
319        header: &SignatureHeader,
320    ) -> (Vec<u8>, Signature) {
321        // this can only fail if we deal with badly formed data.  In that case we
322        // consider that a panic.  Should not happen.
323        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/// Represents the public key of a Relay.
386///
387/// Public keys are based on ed25519 but this should be considered an
388/// implementation detail for now.  We only ever represent public keys
389/// on the wire as opaque ascii encoded strings of arbitrary format or length.
390#[derive(Clone, Eq, PartialEq)]
391pub struct PublicKey {
392    inner: ed25519_dalek::VerifyingKey,
393}
394
395impl PublicKey {
396    /// Verifies the signature and returns the embedded signature header.
397    ///
398    /// Returns [`SignatureError`] when the signature cannot be verified.
399    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            // Default to the regular algorithm for backwards compatibility.
428            .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    /// Unpacks signed data and returns it with header.
456    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
504/// Generates an Relay ID.
505pub fn generate_relay_id() -> RelayId {
506    Uuid::new_v4()
507}
508
509/// Generates a secret + public key pair.
510pub 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/// An encoded and signed `RegisterState`.
522///
523/// This signature can be used by the upstream server to ensure that the downstream client did not
524/// tamper with the token without keeping state between requests. For more information, see
525/// `RegisterState`.
526///
527/// The format and contents of `SignedRegisterState` are intentionally opaque. Downstream clients
528/// do not need to interpret it, and the upstream can change its contents at any time. Parsing and
529/// validation is only performed on the upstream.
530///
531/// In the current implementation, the serialized state has the format `{state}:{signature}`, where
532/// each component is:
533///  - `state`: A URL-safe base64 encoding of the JSON serialized `RegisterState`.
534///  - `signature`: A URL-safe base64 encoding of the SHA512 HMAC of the encoded state.
535///
536/// To create a signed state, use `RegisterChallenge::sign`. To validate the signature and read
537/// the state, use `SignedRegisterChallenge::unpack`. In both cases, a secret for signing has to be
538/// supplied.
539#[derive(Clone, Debug, Deserialize, Serialize)]
540pub struct SignedRegisterState(String);
541
542impl SignedRegisterState {
543    /// Creates an Hmac instance for signing the `RegisterState`.
544    fn mac(secret: &[u8]) -> Hmac<Sha512> {
545        Hmac::new_from_slice(secret).expect("HMAC takes variable keys")
546    }
547
548    /// Signs the given `RegisterState` and serializes it into a single string.
549    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    /// Splits the signed state into the encoded state and encoded signature.
561    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    /// Returns the string representation of the token.
567    pub fn as_str(&self) -> &str {
568        self.0.as_str()
569    }
570
571    /// Unpacks the encoded state and validates the signature.
572    ///
573    /// The timestamp in the state is validated against the current timestamp.
574    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/// A state structure containing relevant information from `RegisterRequest`.
609///
610/// This structure is used to carry over information between the downstream register request and
611/// register response. In addition to identifying information, it contains a random bit to avoid
612/// replay attacks.
613#[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    /// Returns the timestamp at which the challenge was created.
624    pub fn timestamp(&self) -> DateTime<Utc> {
625        self.timestamp
626    }
627
628    /// Returns the identifier of the requesting downstream Relay.
629    pub fn relay_id(&self) -> RelayId {
630        self.relay_id
631    }
632
633    /// Returns the public key of the requesting downstream Relay.
634    pub fn public_key(&self) -> &PublicKey {
635        &self.public_key
636    }
637}
638
639/// Generates a new random token for the register state.
640fn 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/// Represents a request for registration with the upstream.
648///
649/// This is created if the Relay signs in for the first time.  The server needs
650/// to respond to this request with a unique token that is then used to sign
651/// the response.
652#[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    /// Creates a new request to register an Relay upstream.
662    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    /// Unpacks a signed register request for bootstrapping.
671    ///
672    /// This unpacks the embedded public key first, then verifies if the
673    /// self signature was made by that public key.  If all is well then
674    /// the data is returned.
675    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    /// Returns the Relay ID of the registering Relay.
687    pub fn relay_id(&self) -> RelayId {
688        self.relay_id
689    }
690
691    /// Returns the new public key of registering Relay.
692    pub fn public_key(&self) -> &PublicKey {
693        &self.public_key
694    }
695
696    /// Creates a register challenge for this request.
697    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/// Represents the response the server is supposed to send to a register request.
713#[derive(Serialize, Deserialize, Debug)]
714pub struct RegisterChallenge {
715    relay_id: RelayId,
716    token: SignedRegisterState,
717}
718
719impl RegisterChallenge {
720    /// Returns the Relay ID of the registering Relay.
721    pub fn relay_id(&self) -> &RelayId {
722        &self.relay_id
723    }
724
725    /// Returns the token that needs signing.
726    pub fn token(&self) -> &str {
727        self.token.as_str()
728    }
729
730    /// Creates a register response.
731    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/// Represents a response to a register challenge.
741///
742/// The response contains the same data as the register challenge. By signing this payload
743/// successfully, this Relay authenticates with the upstream.
744#[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    /// Unpacks the register response and validates signatures.
754    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    /// Returns the Relay ID of the registering Relay.
772    pub fn relay_id(&self) -> RelayId {
773        self.relay_id
774    }
775
776    /// Returns the token that needs signing.
777    pub fn token(&self) -> &str {
778        self.token.as_str()
779    }
780
781    /// Returns the version of the registering Relay.
782    pub fn version(&self) -> RelayVersion {
783        self.version
784    }
785}
786
787/// A wrapper around a String that represents a signature.
788#[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    /// Verifies the signature against any of the provided public keys.
799    ///
800    /// Returns `true` if the signature is valid with one of the given
801    /// public keys and satisfies the timestamp constraints defined by `start_time`
802    /// and `max_age`.
803    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    /// Verifies the signature using the specified public key.
816    ///
817    /// The signature is considered valid if it can be verified using the given
818    /// public key and its embedded timestamp falls within the valid time range,
819    /// starting from `start_time` and not exceeding `max_age`.
820    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    /// Returns a borrowed view of the signature as a `SignatureRef`.
831    ///
832    /// This method provides a lightweight reference wrapper over the internal
833    /// signature data.
834    pub fn as_signature_ref(&self) -> SignatureRef<'_> {
835        SignatureRef(self.0.as_str())
836    }
837}
838
839/// A borrowed reference to a signature string used for validation.
840///
841/// `SignatureRef` provides a view into the signature data as a string slice,
842/// allowing verification to work with borrowed data without unnecessary allocations.
843/// This type is typically obtained by borrowing from an owned [`Signature`].
844pub struct SignatureRef<'a>(pub &'a str);
845
846/// Verifies a timestamp `ts` is not in the future and not expired.
847fn 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        // initial setup
948        let relay_id = generate_relay_id();
949        let (sk, pk) = generate_key_pair();
950
951        // create a register request
952        let request = RegisterRequest::new(&relay_id, &pk);
953
954        // sign it
955        let (request_bytes, request_sig) = sk.pack(request);
956
957        // attempt to get the data through bootstrap unpacking.
958        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        // create a challenge
971        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        // check the challenge contains the expected info
977        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        // create a response from the challenge
983        let response = challenge.into_response();
984
985        // sign and unsign it
986        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    /// This is a pseudo-test to easily generate the strings used by test_auth.py
1002    /// You can copy the output to the top of the test_auth.py when there are changes in the
1003    /// exchanged authentication structures.
1004    /// It follows test_registration but instead of asserting it prints the strings
1005    #[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        // initial setup
1012        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        // create a register request
1018        let request = RegisterRequest::new(&relay_id, &pk);
1019        println!("REQUEST = b'{}'", serde_json::to_string(&request).unwrap());
1020
1021        // sign it
1022        let (request_bytes, request_sig) = sk.pack(&request);
1023        println!("REQUEST_SIG = \"{request_sig}\"");
1024
1025        // attempt to get the data through bootstrap unpacking.
1026        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        // create a challenge
1037        let challenge = request.into_challenge(upstream_secret);
1038        let challenge_token = challenge.token().to_owned();
1039        println!("TOKEN = \"{challenge_token}\"");
1040
1041        // create a response from the challenge
1042        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 we can still deserialize an old response that does not contain the version
1053    #[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        // Regression test against unintentional changes.
1074        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        // Every version must be supported at the moment.
1093        // This test can be changed when dropping support for older versions.
1094        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        // The signature is valid in general
1128        let verified = signature
1129            .verify(&[], &pair.1, start_time, Duration::seconds(10))
1130            .unwrap();
1131        assert_eq!(verified.timestamp(), start_time);
1132
1133        // Future timestamps within the allowed clock skew are valid.
1134        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        // Future timestamps beyond the allowed clock skew are invalid.
1145        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        // Signature is no longer valid because too much time elapsed
1156        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        // Signature still valid after 1 second
1183        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        // Signature is no longer valid because too much time elapsed
1192        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        // Signature is valid (and verification doesn't panic) with `Duration::MAX`.
1202        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        // TestHeader struct is used to mimic old version that do not have
1233        // the `signature_variant` fields.
1234        #[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}