@@ -2,13 +2,19 @@ package htmsig_test
22
33import (
44 "context"
5+ "crypto/rand"
6+ "crypto/rsa"
57 "net/http"
68 "net/http/httptest"
79 "net/url"
810 "strings"
911 "testing"
12+ "time"
1013
14+ "github.com/lestrrat-go/htmsig"
1115 "github.com/lestrrat-go/htmsig/component"
16+ "github.com/lestrrat-go/htmsig/input"
17+ htmsighttp "github.com/lestrrat-go/htmsig/http"
1218 "github.com/stretchr/testify/require"
1319)
1420
@@ -476,3 +482,104 @@ func TestServerSideTargetURIVsClientSide(t *testing.T) {
476482 require .Equal (t , expectedTargetURI , serverSideValue , "Server-side should match RFC" )
477483 require .Equal (t , clientSideValue , serverSideValue , "Server-side and client-side should match" )
478484}
485+
486+ func TestSignatureExpirationChecking (t * testing.T ) {
487+ // Generate RSA key for testing
488+ privateKey , err := rsa .GenerateKey (rand .Reader , 2048 )
489+ require .NoError (t , err )
490+
491+ // Create an HTTP request
492+ req , err := http .NewRequest ("POST" , "https://example.com/test" , nil )
493+ require .NoError (t , err )
494+ req .Header .Set ("Content-Type" , "application/json" )
495+ req .Header .Set ("Date" , "Tue, 20 Apr 2021 02:07:55 GMT" )
496+
497+ // Use a fixed time for deterministic testing
498+ fixedTime := time .Date (2024 , 1 , 1 , 0 , 0 , 0 , 0 , time .UTC )
499+ staticClock := htmsighttp .FixedClock (fixedTime )
500+
501+ // Test cases
502+ tests := []struct {
503+ name string
504+ expiresOffset time.Duration // offset from now to set expiration
505+ validateExpires bool // whether to enable expiration validation
506+ expectError bool // whether verification should fail
507+ }{
508+ {
509+ name : "Valid signature without expiration" ,
510+ expiresOffset : 0 , // no expiration set
511+ validateExpires : true ,
512+ expectError : false ,
513+ },
514+ {
515+ name : "Valid signature with future expiration" ,
516+ expiresOffset : time .Hour , // expires in 1 hour from fixed time
517+ validateExpires : true ,
518+ expectError : false ,
519+ },
520+ {
521+ name : "Expired signature with validation enabled" ,
522+ expiresOffset : - time .Hour , // expired 1 hour before fixed time
523+ validateExpires : true ,
524+ expectError : true ,
525+ },
526+ {
527+ name : "Expired signature with validation disabled" ,
528+ expiresOffset : - time .Hour , // expired 1 hour before fixed time
529+ validateExpires : false ,
530+ expectError : false ,
531+ },
532+ }
533+
534+ for _ , tt := range tests {
535+ t .Run (tt .name , func (t * testing.T ) {
536+ // Create signature definition
537+ builder := input .NewDefinitionBuilder ().
538+ Label ("test-sig" ).
539+ Components (
540+ component .Method (),
541+ component .TargetURI (),
542+ component .New ("content-type" ),
543+ component .New ("date" ),
544+ ).
545+ KeyID ("test-key-id" )
546+
547+ // Set expiration if specified
548+ if tt .expiresOffset != 0 {
549+ expiresTime := fixedTime .Add (tt .expiresOffset )
550+ builder = builder .ExpiresTime (expiresTime )
551+ }
552+
553+ def , err := builder .Build ()
554+ require .NoError (t , err )
555+
556+ // Create input value and sign the request
557+ inputValue := input .NewValueBuilder ().AddDefinition (def ).MustBuild ()
558+ ctx := component .WithRequestInfoFromHTTP (context .Background (), req )
559+ err = htmsig .SignRequest (ctx , req .Header , inputValue , privateKey )
560+ require .NoError (t , err )
561+
562+ // Create key resolver - use StaticKeyResolver since we only have one key
563+ keyResolver := htmsighttp .StaticKeyResolver (& privateKey .PublicKey )
564+
565+ // Create verification options
566+ var options []htmsig.VerifyOption
567+ if tt .validateExpires {
568+ options = append (options , htmsig .WithValidateExpires (true ))
569+ }
570+ options = append (options , htmsig .WithClock (staticClock ))
571+
572+ // Verify the signature
573+ ctx = component .WithRequestInfoFromHTTP (context .Background (), req )
574+ err = htmsig .VerifyRequest (ctx , req .Header , keyResolver , options ... )
575+
576+ if tt .expectError {
577+ require .Error (t , err , "Expected verification to fail for expired signature" )
578+ require .Contains (t , err .Error (), "signature expired" , "Error should mention expiration" )
579+ } else {
580+ require .NoError (t , err , "Expected verification to succeed" )
581+ }
582+ })
583+ }
584+ }
585+
0 commit comments