@@ -206,19 +206,44 @@ pub fn sign_at<E: Encode, A: Encode>(
206206
207207/// verify_detached validates a COSE_Sign1 digital signature with a detached payload.
208208///
209+ /// Uses the current system time for drift checking. For testing or custom
210+ /// timestamps, use [`verify_detached_at`].
211+ ///
209212/// - `msg_to_check`: The serialized COSE_Sign1 structure (with null payload)
210213/// - `msg_to_auth`: The same message used during signing (verified but not embedded)
211214/// - `verifier`: The xDSA public key to verify against
212215/// - `domain`: Application domain for replay protection
213216/// - `max_drift`: Signatures more in the past or future are rejected
214- ///
215- /// Returns `()` if verification succeeds.
216217pub fn verify_detached < A : Encode > (
217218 msg_to_check : & [ u8 ] ,
218219 msg_to_auth : A ,
219220 verifier : & xdsa:: PublicKey ,
220221 domain : & [ u8 ] ,
221222 max_drift : Option < u64 > ,
223+ ) -> Result < ( ) , Error > {
224+ let now = SystemTime :: now ( )
225+ . duration_since ( UNIX_EPOCH )
226+ . expect ( "system time before Unix epoch" )
227+ . as_secs ( ) as i64 ;
228+ verify_detached_at ( msg_to_check, msg_to_auth, verifier, domain, max_drift, now)
229+ }
230+
231+ /// verify_detached_at validates a COSE_Sign1 digital signature with a detached payload
232+ /// and an explicit current time for drift checking.
233+ ///
234+ /// - `msg_to_check`: The serialized COSE_Sign1 structure (with null payload)
235+ /// - `msg_to_auth`: The same message used during signing (verified but not embedded)
236+ /// - `verifier`: The xDSA public key to verify against
237+ /// - `domain`: Application domain for replay protection
238+ /// - `max_drift`: Signatures more in the past or future are rejected
239+ /// - `now`: Unix timestamp in seconds to use for drift checking
240+ pub fn verify_detached_at < A : Encode > (
241+ msg_to_check : & [ u8 ] ,
242+ msg_to_auth : A ,
243+ verifier : & xdsa:: PublicKey ,
244+ domain : & [ u8 ] ,
245+ max_drift : Option < u64 > ,
246+ now : i64 ,
222247) -> Result < ( ) , Error > {
223248 // Restrict the user's domain to the context of this library
224249 let info = [ DOMAIN_PREFIX , domain] . concat ( ) ;
@@ -236,10 +261,6 @@ pub fn verify_detached<A: Encode>(
236261
237262 // Check signature timestamp drift if max_drift is specified
238263 if let Some ( max) = max_drift {
239- let now = SystemTime :: now ( )
240- . duration_since ( UNIX_EPOCH )
241- . expect ( "system time before Unix epoch" )
242- . as_secs ( ) as i64 ;
243264 let drift = ( now - header. timestamp ) . unsigned_abs ( ) ;
244265 if drift > max {
245266 return Err ( Error :: StaleSignature ( drift, max) ) ;
@@ -265,6 +286,9 @@ pub fn verify_detached<A: Encode>(
265286
266287/// verify validates a COSE_Sign1 digital signature and returns the embedded payload.
267288///
289+ /// Uses the current system time for drift checking. For testing or custom
290+ /// timestamps, use [`verify_at`].
291+ ///
268292/// - `msg_to_check`: The serialized COSE_Sign1 structure
269293/// - `msg_to_auth`: The same additional authenticated data used during signing
270294/// - `verifier`: The xDSA public key to verify against
@@ -278,6 +302,32 @@ pub fn verify<E: Decode, A: Encode>(
278302 verifier : & xdsa:: PublicKey ,
279303 domain : & [ u8 ] ,
280304 max_drift : Option < u64 > ,
305+ ) -> Result < E , Error > {
306+ let now = SystemTime :: now ( )
307+ . duration_since ( UNIX_EPOCH )
308+ . expect ( "system time before Unix epoch" )
309+ . as_secs ( ) as i64 ;
310+ verify_at ( msg_to_check, msg_to_auth, verifier, domain, max_drift, now)
311+ }
312+
313+ /// verify_at validates a COSE_Sign1 digital signature and returns the embedded payload,
314+ /// using an explicit current time for drift checking.
315+ ///
316+ /// - `msg_to_check`: The serialized COSE_Sign1 structure
317+ /// - `msg_to_auth`: The same additional authenticated data used during signing
318+ /// - `verifier`: The xDSA public key to verify against
319+ /// - `domain`: Application domain for replay protection
320+ /// - `max_drift`: Signatures more in the past or future are rejected
321+ /// - `now`: Unix timestamp in seconds to use for drift checking
322+ ///
323+ /// Returns the CBOR-decoded embedded payload if verification succeeds.
324+ pub fn verify_at < E : Decode , A : Encode > (
325+ msg_to_check : & [ u8 ] ,
326+ msg_to_auth : A ,
327+ verifier : & xdsa:: PublicKey ,
328+ domain : & [ u8 ] ,
329+ max_drift : Option < u64 > ,
330+ now : i64 ,
281331) -> Result < E , Error > {
282332 // Restrict the user's domain to the context of this library
283333 let info = [ DOMAIN_PREFIX , domain] . concat ( ) ;
@@ -294,10 +344,6 @@ pub fn verify<E: Decode, A: Encode>(
294344
295345 // Check signature timestamp drift if max_drift is specified
296346 if let Some ( max) = max_drift {
297- let now = SystemTime :: now ( )
298- . duration_since ( UNIX_EPOCH )
299- . expect ( "system time before Unix epoch" )
300- . as_secs ( ) as i64 ;
301347 let drift = ( now - header. timestamp ) . unsigned_abs ( ) ;
302348 if drift > max {
303349 return Err ( Error :: StaleSignature ( drift, max) ) ;
@@ -420,6 +466,9 @@ pub fn seal_at<E: Encode, A: Encode>(
420466
421467/// open decrypts and verifies a sealed message.
422468///
469+ /// Uses the current system time for drift checking. For testing or custom
470+ /// timestamps, use [`open_at`].
471+ ///
423472/// - `msg_to_open`: The serialized COSE_Encrypt0 structure
424473/// - `msg_to_auth`: The same additional authenticated data used during sealing
425474/// - `recipient`: The xHPKE secret key to decrypt with
@@ -435,6 +484,42 @@ pub fn open<E: Decode, A: Encode>(
435484 sender : & xdsa:: PublicKey ,
436485 domain : & [ u8 ] ,
437486 max_drift : Option < u64 > ,
487+ ) -> Result < E , Error > {
488+ let now = SystemTime :: now ( )
489+ . duration_since ( UNIX_EPOCH )
490+ . expect ( "system time before Unix epoch" )
491+ . as_secs ( ) as i64 ;
492+ open_at (
493+ msg_to_open,
494+ msg_to_auth,
495+ recipient,
496+ sender,
497+ domain,
498+ max_drift,
499+ now,
500+ )
501+ }
502+
503+ /// open_at decrypts and verifies a sealed message with an explicit current time
504+ /// for drift checking.
505+ ///
506+ /// - `msg_to_open`: The serialized COSE_Encrypt0 structure
507+ /// - `msg_to_auth`: The same additional authenticated data used during sealing
508+ /// - `recipient`: The xHPKE secret key to decrypt with
509+ /// - `sender`: The xDSA public key to verify the signature against
510+ /// - `domain`: Application domain for HPKE key derivation
511+ /// - `max_drift`: Signatures more in the past or future are rejected
512+ /// - `now`: Unix timestamp in seconds to use for drift checking
513+ ///
514+ /// Returns the CBOR-decoded payload if decryption and verification succeed.
515+ pub fn open_at < E : Decode , A : Encode > (
516+ msg_to_open : & [ u8 ] ,
517+ msg_to_auth : A ,
518+ recipient : & xhpke:: SecretKey ,
519+ sender : & xdsa:: PublicKey ,
520+ domain : & [ u8 ] ,
521+ max_drift : Option < u64 > ,
522+ now : i64 ,
438523) -> Result < E , Error > {
439524 // Pre-encode for EncStructure (which needs raw bytes for external_aad)
440525 let msg_to_auth = cbor:: encode ( msg_to_auth) ;
@@ -474,7 +559,14 @@ pub fn open<E: Decode, A: Encode>(
474559 . map_err ( |e| Error :: DecryptionFailed ( e. to_string ( ) ) ) ?;
475560
476561 // Verify the signature and extract the payload
477- let raw: Raw = verify :: < Raw , _ > ( & msg_to_check, & Raw ( msg_to_auth) , sender, domain, max_drift) ?;
562+ let raw: Raw = verify_at :: < Raw , _ > (
563+ & msg_to_check,
564+ & Raw ( msg_to_auth) ,
565+ sender,
566+ domain,
567+ max_drift,
568+ now,
569+ ) ?;
478570 Ok ( cbor:: decode ( & raw . 0 ) ?)
479571}
480572
0 commit comments