@@ -11,6 +11,7 @@ import (
1111 "crypto/tls"
1212 "crypto/x509"
1313 "encoding/hex"
14+ "encoding/pem"
1415 "errors"
1516 "fmt"
1617 "io"
@@ -2486,6 +2487,272 @@ func TestSessionResume(t *testing.T) {
24862487 }
24872488 _ = res .c .Close ()
24882489 })
2490+
2491+ t .Run ("resumed client cert" , func (t * testing.T ) {
2492+ ctx , cancel := context .WithTimeout (context .Background (), 10 * time .Second )
2493+ defer cancel ()
2494+
2495+ type result struct {
2496+ c * Conn
2497+ err error
2498+ }
2499+ clientRes := make (chan result , 1 )
2500+
2501+ commonCert , err := selfsign .GenerateSelfSignedWithDNS ("example.com" )
2502+
2503+ certPool := x509 .NewCertPool ()
2504+ certPool .AppendCertsFromPEM (pem .EncodeToMemory (& pem.Block {Type : "CERTIFICATE" , Bytes : commonCert .Certificate [0 ]}))
2505+
2506+ ss := & memSessStore {}
2507+
2508+ id , _ := hex .DecodeString ("9b9fc92255634d9fb109febed42166717bb8ded8c738ba71bc7f2a0d9dae0306" )
2509+ secret , _ := hex .DecodeString ("2e942a37aca5241deb2295b5fcedac221c7078d2503d2b62aeb48c880d7da73c001238b708559686b9da6e829c05ead7" )
2510+
2511+ s := Session {ID : id , Secret : secret }
2512+
2513+ ca , cb := dpipe .Pipe ()
2514+
2515+ _ = ss .Set (id , s )
2516+ _ = ss .Set ([]byte (ca .RemoteAddr ().String ()+ "_" + commonCert .Leaf .Subject .CommonName ), s )
2517+
2518+ go func () {
2519+ config := & Config {
2520+ CipherSuites : []CipherSuiteID {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 },
2521+ ServerName : commonCert .Leaf .Subject .CommonName ,
2522+ SessionStore : ss ,
2523+ RootCAs : certPool ,
2524+ Certificates : nil , // Client shouldn't need to send a cert to resume a session
2525+ MTU : 100 ,
2526+ }
2527+ c , err := ClientWithContext (ctx , ca , config )
2528+ clientRes <- result {c , err }
2529+ }()
2530+
2531+ config := & Config {
2532+ CipherSuites : []CipherSuiteID {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 },
2533+ SessionStore : ss ,
2534+ ClientCAs : certPool ,
2535+ Certificates : []tls.Certificate {commonCert },
2536+ MTU : 100 ,
2537+ ClientAuth : RequireAndVerifyClientCert ,
2538+ }
2539+ server , err := testServer (ctx , cb , config , true )
2540+ if err != nil {
2541+ t .Fatalf ("TestSessionResume: Server failed(%v)" , err )
2542+ }
2543+
2544+ actualSessionID := server .ConnectionState ().SessionID
2545+ actualMasterSecret := server .ConnectionState ().masterSecret
2546+ if ! bytes .Equal (actualSessionID , id ) {
2547+ t .Errorf ("TestSessionResumetion: SessionID Mismatch: expected(%v) actual(%v)" , id , actualSessionID )
2548+ }
2549+ if ! bytes .Equal (actualMasterSecret , secret ) {
2550+ t .Errorf ("TestSessionResumetion: masterSecret Mismatch: expected(%v) actual(%v)" , secret , actualMasterSecret )
2551+ }
2552+
2553+ defer func () {
2554+ _ = server .Close ()
2555+ }()
2556+
2557+ res := <- clientRes
2558+ if res .err != nil {
2559+ t .Fatal (res .err )
2560+ }
2561+ _ = res .c .Close ()
2562+ })
2563+
2564+ t .Run ("new session client cert" , func (t * testing.T ) {
2565+ ctx , cancel := context .WithTimeout (context .Background (), 10 * time .Second )
2566+ defer cancel ()
2567+
2568+ type result struct {
2569+ c * Conn
2570+ err error
2571+ }
2572+ clientRes := make (chan result , 1 )
2573+
2574+ commonCert , err := selfsign .GenerateSelfSignedWithDNS ("example.com" )
2575+
2576+ certPool := x509 .NewCertPool ()
2577+ certPool .AppendCertsFromPEM (pem .EncodeToMemory (& pem.Block {Type : "CERTIFICATE" , Bytes : commonCert .Certificate [0 ]}))
2578+
2579+ s1 := & memSessStore {}
2580+ s2 := & memSessStore {}
2581+
2582+ ca , cb := dpipe .Pipe ()
2583+ go func () {
2584+ config := & Config {
2585+ ServerName : commonCert .Leaf .Subject .CommonName ,
2586+ SessionStore : s1 ,
2587+ RootCAs : certPool ,
2588+ Certificates : []tls.Certificate {commonCert },
2589+ }
2590+ c , err := ClientWithContext (ctx , ca , config )
2591+ clientRes <- result {c , err }
2592+ }()
2593+
2594+ config := & Config {
2595+ SessionStore : s2 ,
2596+ ClientAuth : RequireAndVerifyClientCert ,
2597+ ClientCAs : certPool ,
2598+ Certificates : []tls.Certificate {commonCert },
2599+ }
2600+ server , err := testServer (ctx , cb , config , false )
2601+ if err != nil {
2602+ t .Fatalf ("TestSessionResumetion: Server failed(%v)" , err )
2603+ }
2604+
2605+ actualSessionID := server .ConnectionState ().SessionID
2606+ actualMasterSecret := server .ConnectionState ().masterSecret
2607+ ss , _ := s2 .Get (actualSessionID )
2608+ if ! bytes .Equal (actualMasterSecret , ss .Secret ) {
2609+ t .Errorf ("TestSessionResumetion: masterSecret Mismatch: expected/actual:\n (%v)\n (%v)" , ss .Secret , actualMasterSecret )
2610+ }
2611+
2612+ if ss .Expiry .Unix () != commonCert .Leaf .NotAfter .Unix () {
2613+ t .Errorf ("TestSessionResumption: expected server session store to contain certificate expiry" )
2614+ }
2615+
2616+ defer func () {
2617+ _ = server .Close ()
2618+ }()
2619+
2620+ res := <- clientRes
2621+ if res .err != nil {
2622+ t .Fatal (res .err )
2623+ }
2624+ cs , _ := s1 .Get ([]byte (ca .RemoteAddr ().String () + "_" + commonCert .Leaf .Subject .CommonName ))
2625+ if ! bytes .Equal (actualMasterSecret , cs .Secret ) {
2626+ t .Errorf ("TestSessionResumetion: masterSecret Mismatch: expected/actual\n (%v)\n (%v)" , cs .Secret , actualMasterSecret )
2627+ }
2628+
2629+ if cs .Expiry .Unix () != commonCert .Leaf .NotAfter .Unix () {
2630+ t .Errorf ("TestSessionResumption: expected client session store to contain certificate expiry" )
2631+ }
2632+
2633+ _ = res .c .Close ()
2634+ })
2635+
2636+ t .Run ("expire client cert session" , func (t * testing.T ) {
2637+ ctx , cancel := context .WithTimeout (context .Background (), 10 * time .Second )
2638+ defer cancel ()
2639+
2640+ type result struct {
2641+ c * Conn
2642+ err error
2643+ }
2644+ clientRes := make (chan result , 1 )
2645+
2646+ commonCert , err := selfsign .GenerateSelfSignedWithDNS ("example.com" )
2647+
2648+ if err != nil {
2649+ t .Fatal (err )
2650+ }
2651+ certPool := x509 .NewCertPool ()
2652+ certPool .AppendCertsFromPEM (pem .EncodeToMemory (& pem.Block {Type : "CERTIFICATE" , Bytes : commonCert .Certificate [0 ]}))
2653+
2654+ ss := & memSessStore {}
2655+
2656+ id , _ := hex .DecodeString ("9b9fc92255634d9fb109febed42166717bb8ded8c738ba71bc7f2a0d9dae0306" )
2657+ secret , _ := hex .DecodeString ("2e942a37aca5241deb2295b5fcedac221c7078d2503d2b62aeb48c880d7da73c001238b708559686b9da6e829c05ead7" )
2658+
2659+ oldClientSessionTime := time .Now ().Add (time .Hour )
2660+ clientSession := Session {
2661+ ID : id ,
2662+ Secret : secret ,
2663+ Expiry : oldClientSessionTime ,
2664+ }
2665+
2666+ expiredServerSession := Session {
2667+ ID : id ,
2668+ Secret : secret ,
2669+ Expiry : time .Now ().Add (- time .Hour ), // server should treat this as expired session and force a new cert verification
2670+ }
2671+
2672+ ca , cb := dpipe .Pipe ()
2673+
2674+ _ = ss .Set (id , expiredServerSession )
2675+ _ = ss .Set ([]byte (ca .RemoteAddr ().String ()+ "_" + commonCert .Leaf .Subject .CommonName ), clientSession )
2676+
2677+ go func () {
2678+ config := & Config {
2679+ CipherSuites : []CipherSuiteID {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 },
2680+ ServerName : commonCert .Leaf .Subject .CommonName ,
2681+ SessionStore : ss ,
2682+ RootCAs : certPool ,
2683+ Certificates : []tls.Certificate {commonCert },
2684+ MTU : 1200 , // MTU must be able to fit cert chain in one packet
2685+ }
2686+ c , err := ClientWithContext (ctx , ca , config )
2687+ clientRes <- result {c , err }
2688+ }()
2689+
2690+ config := & Config {
2691+ CipherSuites : []CipherSuiteID {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 },
2692+ SessionStore : ss ,
2693+ ClientCAs : certPool ,
2694+ Certificates : []tls.Certificate {commonCert },
2695+ MTU : 1200 ,
2696+ ClientAuth : RequireAndVerifyClientCert ,
2697+ }
2698+ server , err := testServer (ctx , cb , config , false )
2699+ if err != nil {
2700+ t .Fatalf ("TestSessionResume: Server failed(%v)" , err )
2701+ }
2702+
2703+ actualSessionID := server .ConnectionState ().SessionID
2704+ actualMasterSecret := server .ConnectionState ().masterSecret
2705+ if bytes .Equal (actualSessionID , id ) {
2706+ t .Errorf ("TestSessionResumption: SessionID Mismatch: expected new session ID(%v) actual(%v)" , id , actualSessionID )
2707+ }
2708+
2709+ if bytes .Equal (actualMasterSecret , secret ) {
2710+ t .Errorf ("TestSessionResumption: masterSecret Mismatch: expected new master secret (%v) actual(%v)" , secret , actualMasterSecret )
2711+ }
2712+
2713+ defer func () {
2714+ _ = server .Close ()
2715+ }()
2716+
2717+ res := <- clientRes
2718+ if res .err != nil {
2719+ t .Fatal (res .err )
2720+ }
2721+
2722+ _ , ok := ss .Map .Load (hex .EncodeToString (expiredServerSession .ID ))
2723+ if ok {
2724+ t .Errorf ("expected server to have deleted session" )
2725+ }
2726+
2727+ cSess , ok := ss .Map .Load (hex .EncodeToString ([]byte (ca .RemoteAddr ().String () + "_" + commonCert .Leaf .Subject .CommonName )))
2728+ if ! ok {
2729+ t .Errorf ("expected client store to have cached new session ID" )
2730+ }
2731+
2732+ newClientSession := cSess .(Session )
2733+ if bytes .Equal (secret , newClientSession .Secret ) {
2734+ t .Errorf ("expected : expected client session store to contain new master secret (%v) actual(%v)" , secret , newClientSession .Secret )
2735+ }
2736+
2737+ if newClientSession .Expiry .Unix () == oldClientSessionTime .Unix () {
2738+ t .Errorf ("expected new client session to have updated" )
2739+ }
2740+
2741+ if newClientSession .Expiry .Unix () != commonCert .Leaf .NotAfter .Unix () {
2742+ t .Errorf ("expected new client session to expire with client cert" )
2743+ }
2744+
2745+ sSess , ok := ss .Map .Load (hex .EncodeToString (newClientSession .ID ))
2746+ if ! ok {
2747+ t .Errorf ("expected server store to have cached new client session ID" )
2748+ }
2749+ newServerSession := sSess .(Session )
2750+
2751+ if ! bytes .Equal (newServerSession .Secret , newClientSession .Secret ) {
2752+ t .Errorf ("expected : expected session store to contain new shared secret (%v) actual(%v)" , newServerSession .Secret , newClientSession .Secret )
2753+ }
2754+ _ = res .c .Close ()
2755+ })
24892756}
24902757
24912758type memSessStore struct {
@@ -2507,7 +2774,16 @@ func (ms *memSessStore) Get(key []byte) (Session, error) {
25072774 return Session {}, nil
25082775 }
25092776
2510- return v .(Session ), nil
2777+ session := v .(Session )
2778+ if session .Expiry .IsZero () {
2779+ return session , nil
2780+ }
2781+
2782+ if time .Now ().After (session .Expiry ) {
2783+ _ = ms .Del (key )
2784+ return Session {}, nil
2785+ }
2786+ return session , nil
25112787}
25122788
25132789func (ms * memSessStore ) Del (key []byte ) error {
0 commit comments