get.pme.sh/pnats@v0.0.0-20240304004023-26bb5a137ed0/server/certidp/ocsp_responder.go (about)

     1  // Copyright 2023 The NATS Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package certidp
    15  
    16  import (
    17  	"encoding/base64"
    18  	"fmt"
    19  	"io"
    20  	"net/http"
    21  	"strings"
    22  	"time"
    23  
    24  	"golang.org/x/crypto/ocsp"
    25  )
    26  
    27  func FetchOCSPResponse(link *ChainLink, opts *OCSPPeerConfig, log *Log) ([]byte, error) {
    28  	if link == nil || link.Leaf == nil || link.Issuer == nil || opts == nil || log == nil {
    29  		return nil, fmt.Errorf(ErrInvalidChainlink)
    30  	}
    31  
    32  	timeout := time.Duration(opts.Timeout * float64(time.Second))
    33  	if timeout <= 0*time.Second {
    34  		timeout = DefaultOCSPResponderTimeout
    35  	}
    36  
    37  	getRequestBytes := func(u string, hc *http.Client) ([]byte, error) {
    38  		resp, err := hc.Get(u)
    39  		if err != nil {
    40  			return nil, err
    41  		}
    42  		defer resp.Body.Close()
    43  		if resp.StatusCode != http.StatusOK {
    44  			return nil, fmt.Errorf(ErrBadResponderHTTPStatus, resp.StatusCode)
    45  		}
    46  		return io.ReadAll(resp.Body)
    47  	}
    48  
    49  	// Request documentation:
    50  	// https://tools.ietf.org/html/rfc6960#appendix-A.1
    51  
    52  	reqDER, err := ocsp.CreateRequest(link.Leaf, link.Issuer, nil)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  
    57  	reqEnc := base64.StdEncoding.EncodeToString(reqDER)
    58  
    59  	responders := *link.OCSPWebEndpoints
    60  
    61  	if len(responders) == 0 {
    62  		return nil, fmt.Errorf(ErrNoAvailOCSPServers)
    63  	}
    64  
    65  	var raw []byte
    66  	hc := &http.Client{
    67  		Timeout: timeout,
    68  	}
    69  	for _, u := range responders {
    70  		url := u.String()
    71  		log.Debugf(DbgMakingCARequest, url)
    72  		url = strings.TrimSuffix(url, "/")
    73  		raw, err = getRequestBytes(fmt.Sprintf("%s/%s", url, reqEnc), hc)
    74  		if err == nil {
    75  			break
    76  		}
    77  	}
    78  	if err != nil {
    79  		return nil, fmt.Errorf(ErrFailedWithAllRequests, err)
    80  	}
    81  
    82  	return raw, nil
    83  }