<commit_before>package ca

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/jmhodges/clock"
	"github.com/miekg/pkcs11"
	"github.com/prometheus/client_golang/prometheus"
	"golang.org/x/crypto/ocsp"

	capb "github.com/letsencrypt/boulder/ca/proto"
	"github.com/letsencrypt/boulder/core"
	berrors "github.com/letsencrypt/boulder/errors"
	"github.com/letsencrypt/boulder/issuance"
	blog "github.com/letsencrypt/boulder/log"
)

// TODO(#5152): Simplify this when we've fully deprecated old-style IssuerIDs.
type ocspIssuerMaps struct {
	byID     map[issuance.IssuerID]*issuance.Issuer
	byNameID map[issuance.IssuerNameID]*issuance.Issuer
}

// ocspImpl provides a backing implementation for the OCSP gRPC service.
type ocspImpl struct {
	capb.UnimplementedOCSPGeneratorServer
	issuers        ocspIssuerMaps
	ocspLifetime   time.Duration
	ocspLogQueue   *ocspLogQueue
	log            blog.Logger
	signatureCount *prometheus.CounterVec
	signErrorCount *prometheus.CounterVec
	clk            clock.Clock
}

// makeOCSPIssuerMaps processes a list of issuers into a set of maps, mapping
// nearly-unique identifiers of those issuers to the issuers themselves. Note
// that, if two issuers have the same nearly-unique ID, the *latter* one in
// the input list "wins".
func makeOCSPIssuerMaps(issuers []*issuance.Issuer) (ocspIssuerMaps, error) {
	issuersByID := make(map[issuance.IssuerID]*issuance.Issuer, len(issuers))
	issuersByNameID := make(map[issuance.IssuerNameID]*issuance.Issuer, len(issuers))
	for _, issuer := range issuers {
		issuersByID[issuer.ID()] = issuer
		issuersByNameID[issuer.Cert.NameID()] = issuer
	}
	return ocspIssuerMaps{issuersByID, issuersByNameID}, nil
}

func NewOCSPImpl(
	issuers []*issuance.Issuer,
	ocspLifetime time.Duration,
	ocspLogMaxLength int,
	ocspLogPeriod time.Duration,
	logger blog.Logger,
	stats prometheus.Registerer,
	signatureCount *prometheus.CounterVec,
	signErrorCount *prometheus.CounterVec,
	clk clock.Clock,
) (*ocspImpl, error) {
	issuersByID := make(map[issuance.IssuerID]*issuance.Issuer, len(issuers))
	for _, issuer := range issuers {
		issuersByID[issuer.ID()] = issuer
	}

	var ocspLogQueue *ocspLogQueue
	if ocspLogMaxLength > 0 {
		ocspLogQueue = newOCSPLogQueue(ocspLogMaxLength, ocspLogPeriod, stats, logger)
	}

	issuerMaps, err := makeOCSPIssuerMaps(issuers)
	if err != nil {
		return nil, err
	}

	oi := &ocspImpl{
		issuers:        issuerMaps,
		ocspLifetime:   ocspLifetime,
		ocspLogQueue:   ocspLogQueue,
		log:            logger,
		signatureCount: signatureCount,
		signErrorCount: signErrorCount,
		clk:            clk,
	}
	return oi, nil
}

// LogOCSPLoop collects OCSP generation log events into bundles, and logs
// them periodically.
func (oi *ocspImpl) LogOCSPLoop() {
	if oi.ocspLogQueue != nil {
		oi.ocspLogQueue.loop()
	}
}

// Stop asks this ocspImpl to shut down. It must be called after the
// corresponding RPC service is shut down and there are no longer any inflight
// RPCs. It will attempt to drain any logging queues (which may block), and will
// return only when done.
func (oi *ocspImpl) Stop() {
	if oi.ocspLogQueue != nil {
		oi.ocspLogQueue.stop()
	}
}

// GenerateOCSP produces a new OCSP response and returns it
func (oi *ocspImpl) GenerateOCSP(ctx context.Context, req *capb.GenerateOCSPRequest) (*capb.OCSPResponse, error) {
	// req.Status, req.Reason, and req.RevokedAt are often 0, for non-revoked certs.
	if core.IsAnyNilOrZero(req, req.Serial, req.IssuerID) {
		return nil, berrors.InternalServerError("Incomplete generate OCSP request")
	}

	serialInt, err := core.StringToSerial(req.Serial)
	if err != nil {
		return nil, err
	}
	serial := serialInt

	issuer, ok := oi.issuers.byNameID[issuance.IssuerNameID(req.IssuerID)]
	if !ok {
		// TODO(#5152): Remove this fallback to old-style IssuerIDs.
		issuer, ok = oi.issuers.byID[issuance.IssuerID(req.IssuerID)]
		if !ok {
			return nil, fmt.Errorf("This CA doesn't have an issuer cert with ID %d", req.IssuerID)
		}
	}

	now := oi.clk.Now().Truncate(time.Hour)
	tbsResponse := ocsp.Response{
		Status:       ocspStatusToCode[req.Status],
		SerialNumber: serial,
		ThisUpdate:   now,
		NextUpdate:   now.Add(oi.ocspLifetime),<commit_msg>Compute OCSP validity interval correctly (#5680)<commit_after>package ca

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/jmhodges/clock"
	"github.com/miekg/pkcs11"
	"github.com/prometheus/client_golang/prometheus"
	"golang.org/x/crypto/ocsp"

	capb "github.com/letsencrypt/boulder/ca/proto"
	"github.com/letsencrypt/boulder/core"
	berrors "github.com/letsencrypt/boulder/errors"
	"github.com/letsencrypt/boulder/issuance"
	blog "github.com/letsencrypt/boulder/log"
)

// TODO(#5152): Simplify this when we've fully deprecated old-style IssuerIDs.
type ocspIssuerMaps struct {
	byID     map[issuance.IssuerID]*issuance.Issuer
	byNameID map[issuance.IssuerNameID]*issuance.Issuer
}

// ocspImpl provides a backing implementation for the OCSP gRPC service.
type ocspImpl struct {
	capb.UnimplementedOCSPGeneratorServer
	issuers        ocspIssuerMaps
	ocspLifetime   time.Duration
	ocspLogQueue   *ocspLogQueue
	log            blog.Logger
	signatureCount *prometheus.CounterVec
	signErrorCount *prometheus.CounterVec
	clk            clock.Clock
}

// makeOCSPIssuerMaps processes a list of issuers into a set of maps, mapping
// nearly-unique identifiers of those issuers to the issuers themselves. Note
// that, if two issuers have the same nearly-unique ID, the *latter* one in
// the input list "wins".
func makeOCSPIssuerMaps(issuers []*issuance.Issuer) (ocspIssuerMaps, error) {
	issuersByID := make(map[issuance.IssuerID]*issuance.Issuer, len(issuers))
	issuersByNameID := make(map[issuance.IssuerNameID]*issuance.Issuer, len(issuers))
	for _, issuer := range issuers {
		issuersByID[issuer.ID()] = issuer
		issuersByNameID[issuer.Cert.NameID()] = issuer
	}
	return ocspIssuerMaps{issuersByID, issuersByNameID}, nil
}

func NewOCSPImpl(
	issuers []*issuance.Issuer,
	ocspLifetime time.Duration,
	ocspLogMaxLength int,
	ocspLogPeriod time.Duration,
	logger blog.Logger,
	stats prometheus.Registerer,
	signatureCount *prometheus.CounterVec,
	signErrorCount *prometheus.CounterVec,
	clk clock.Clock,
) (*ocspImpl, error) {
	issuersByID := make(map[issuance.IssuerID]*issuance.Issuer, len(issuers))
	for _, issuer := range issuers {
		issuersByID[issuer.ID()] = issuer
	}

	var ocspLogQueue *ocspLogQueue
	if ocspLogMaxLength > 0 {
		ocspLogQueue = newOCSPLogQueue(ocspLogMaxLength, ocspLogPeriod, stats, logger)
	}

	issuerMaps, err := makeOCSPIssuerMaps(issuers)
	if err != nil {
		return nil, err
	}

	oi := &ocspImpl{
		issuers:        issuerMaps,
		ocspLifetime:   ocspLifetime,
		ocspLogQueue:   ocspLogQueue,
		log:            logger,
		signatureCount: signatureCount,
		signErrorCount: signErrorCount,
		clk:            clk,
	}
	return oi, nil
}

// LogOCSPLoop collects OCSP generation log events into bundles, and logs
// them periodically.
func (oi *ocspImpl) LogOCSPLoop() {
	if oi.ocspLogQueue != nil {
		oi.ocspLogQueue.loop()
	}
}

// Stop asks this ocspImpl to shut down. It must be called after the
// corresponding RPC service is shut down and there are no longer any inflight
// RPCs. It will attempt to drain any logging queues (which may block), and will
// return only when done.
func (oi *ocspImpl) Stop() {
	if oi.ocspLogQueue != nil {
		oi.ocspLogQueue.stop()
	}
}

// GenerateOCSP produces a new OCSP response and returns it
func (oi *ocspImpl) GenerateOCSP(ctx context.Context, req *capb.GenerateOCSPRequest) (*capb.OCSPResponse, error) {
	// req.Status, req.Reason, and req.RevokedAt are often 0, for non-revoked certs.
	if core.IsAnyNilOrZero(req, req.Serial, req.IssuerID) {
		return nil, berrors.InternalServerError("Incomplete generate OCSP request")
	}

	serialInt, err := core.StringToSerial(req.Serial)
	if err != nil {
		return nil, err
	}
	serial := serialInt

	issuer, ok := oi.issuers.byNameID[issuance.IssuerNameID(req.IssuerID)]
	if !ok {
		// TODO(#5152): Remove this fallback to old-style IssuerIDs.
		issuer, ok = oi.issuers.byID[issuance.IssuerID(req.IssuerID)]
		if !ok {
			return nil, fmt.Errorf("This CA doesn't have an issuer cert with ID %d", req.IssuerID)
		}
	}

	now := oi.clk.Now().Truncate(time.Hour)
	tbsResponse := ocsp.Response{
		Status:       ocspStatusToCode[req.Status],
		SerialNumber: serial,
		ThisUpdate:   now,
		NextUpdate:   now.Add(oi.ocspLifetime - time.Second),
{'fraction_non_alphanumeric': 0.10817506193228736, 'fraction_numerical': 0.0033030553261767133, 'mean_word_length': 4.305381727158949, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6756861', 'n_tokens_mistral': 3239, 'n_tokens_neox': 3069, 'n_words': 725}