2222// validity period.
2323
2424use core:: convert:: TryInto ;
25+ use core:: fmt;
2526
2627use secp256k1:: { hashes:: sha256, schnorr:: Signature , Keypair , Message , Secp256k1 , XOnlyPublicKey } ;
2728
@@ -32,6 +33,7 @@ use secp256k1::{hashes::sha256, schnorr::Signature, Keypair, Message, Secp256k1,
3233///
3334/// This structure ensures that messages are authenticated and valid only within
3435/// a specified time window, using Schnorr signatures over the `secp256k1` elliptic curve.
36+ #[ derive( Debug , PartialEq , Eq ) ]
3537pub struct SignatureNoiseMessage {
3638 // Version of the protocol being used.
3739 pub version : u16 ,
@@ -43,6 +45,23 @@ pub struct SignatureNoiseMessage {
4345 pub signature : [ u8 ; 64 ] ,
4446}
4547
48+ impl fmt:: Display for SignatureNoiseMessage {
49+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
50+ // write header
51+ write ! (
52+ f,
53+ "SignatureNoiseMessage {{ version: {}, valid_from: {}, not_valid_after: {}, signature: " ,
54+ self . version, self . valid_from, self . not_valid_after
55+ ) ?;
56+
57+ // write signature as hex manually
58+ for byte in & self . signature {
59+ write ! ( f, "{:02x}" , byte) ?;
60+ }
61+
62+ write ! ( f, " }}" )
63+ }
64+ }
4665impl From < [ u8 ; 74 ] > for SignatureNoiseMessage {
4766 // Converts a 74-byte array into a [`SignatureNoiseMessage`].
4867 //
@@ -81,7 +100,8 @@ impl SignatureNoiseMessage {
81100 self . verify_with_now ( pk, authority_pk, now)
82101 }
83102
84- /// Verifies the validity and authenticity of the `SignatureNoiseMessage` at a given timestamp.
103+ /// Verifies the validity and authenticity of the `SignatureNoiseMessage` at a given timestamp
104+ /// with 10 seconds of tolerance.
85105 ///
86106 /// See [`Self::verify`] for more details.
87107 ///
@@ -94,8 +114,16 @@ impl SignatureNoiseMessage {
94114 authority_pk : & Option < XOnlyPublicKey > ,
95115 now : u32 ,
96116 ) -> bool {
117+ // Allow the local clock to drift up to 10 seconds ahead or behind.
118+ // See https://github.com/stratum-mining/stratum/issues/2015
119+ const TIME_LEEWAY : u32 = 10 ;
120+
97121 if let Some ( authority_pk) = authority_pk {
98- if self . valid_from <= now && self . not_valid_after >= now {
122+ // Use saturating ops to cap edges (valid_from ≥ 0, not_valid_after ≤ u32::MAX),
123+ // preventing wrap-around and subtle validation bugs with untrusted timestamps.
124+ if self . valid_from . saturating_sub ( TIME_LEEWAY ) <= now
125+ && self . not_valid_after . saturating_add ( TIME_LEEWAY ) >= now
126+ {
99127 let secp = Secp256k1 :: verification_only ( ) ;
100128 let ( m, s) = self . split ( ) ;
101129 // m = SHA-256(version || valid_from || not_valid_after || server_static_key)
0 commit comments