...

Source file src/github.com/google/certificate-transparency-go/trillian/ctfe/handlers.go

Documentation: github.com/google/certificate-transparency-go/trillian/ctfe

     1  // Copyright 2016 Google LLC. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package ctfe
    16  
    17  import (
    18  	"context"
    19  	"crypto"
    20  	"crypto/sha256"
    21  	"encoding/base64"
    22  	"encoding/json"
    23  	"errors"
    24  	"flag"
    25  	"fmt"
    26  	"io"
    27  	"net/http"
    28  	"strconv"
    29  	"strings"
    30  	"sync"
    31  	"time"
    32  
    33  	"github.com/google/certificate-transparency-go/asn1"
    34  	"github.com/google/certificate-transparency-go/tls"
    35  	"github.com/google/certificate-transparency-go/trillian/util"
    36  	"github.com/google/certificate-transparency-go/x509"
    37  	"github.com/google/certificate-transparency-go/x509util"
    38  	"github.com/google/trillian"
    39  	"github.com/google/trillian/monitoring"
    40  	"github.com/google/trillian/types"
    41  	"google.golang.org/grpc/codes"
    42  	"google.golang.org/grpc/status"
    43  	"google.golang.org/protobuf/encoding/prototext"
    44  	"k8s.io/klog/v2"
    45  
    46  	ct "github.com/google/certificate-transparency-go"
    47  )
    48  
    49  var (
    50  	alignGetEntries   = flag.Bool("align_getentries", true, "Enable get-entries request alignment")
    51  	getEntriesMetrics = flag.Bool("getentries_metrics", false, "Export get-entries distribution metrics")
    52  )
    53  
    54  const (
    55  	// HTTP Cache-Control header
    56  	cacheControlHeader = "Cache-Control"
    57  	// Value for Cache-Control header when response contains immutable data, i.e. entries or proofs. Allows the response to be cached for 1 day.
    58  	cacheControlImmutable = "public, max-age=86400"
    59  	// HTTP content type header
    60  	contentTypeHeader string = "Content-Type"
    61  	// MIME content type for JSON
    62  	contentTypeJSON string = "application/json"
    63  	// The name of the JSON response map key in get-roots responses
    64  	jsonMapKeyCertificates string = "certificates"
    65  	// The name of the get-entries start parameter
    66  	getEntriesParamStart = "start"
    67  	// The name of the get-entries end parameter
    68  	getEntriesParamEnd = "end"
    69  	// The name of the get-proof-by-hash parameter
    70  	getProofParamHash = "hash"
    71  	// The name of the get-proof-by-hash tree size parameter
    72  	getProofParamTreeSize = "tree_size"
    73  	// The name of the get-sth-consistency first snapshot param
    74  	getSTHConsistencyParamFirst = "first"
    75  	// The name of the get-sth-consistency second snapshot param
    76  	getSTHConsistencyParamSecond = "second"
    77  	// The name of the get-entry-and-proof index parameter
    78  	getEntryAndProofParamLeafIndex = "leaf_index"
    79  	// The name of the get-entry-and-proof tree size parameter
    80  	getEntryAndProofParamTreeSize = "tree_size"
    81  )
    82  
    83  var (
    84  	// MaxGetEntriesAllowed is the number of entries we allow in a get-entries request
    85  	MaxGetEntriesAllowed int64 = 1000
    86  
    87  	// Use an explicitly empty slice for empty proofs so it gets JSON-encoded as
    88  	// '[]' rather than 'null'.
    89  	emptyProof = make([][]byte, 0)
    90  )
    91  
    92  // EntrypointName identifies a CT entrypoint as defined in section 4 of RFC 6962.
    93  type EntrypointName string
    94  
    95  // Constants for entrypoint names, as exposed in statistics/logging.
    96  const (
    97  	AddChainName          = EntrypointName("AddChain")
    98  	AddPreChainName       = EntrypointName("AddPreChain")
    99  	GetSTHName            = EntrypointName("GetSTH")
   100  	GetSTHConsistencyName = EntrypointName("GetSTHConsistency")
   101  	GetProofByHashName    = EntrypointName("GetProofByHash")
   102  	GetEntriesName        = EntrypointName("GetEntries")
   103  	GetRootsName          = EntrypointName("GetRoots")
   104  	GetEntryAndProofName  = EntrypointName("GetEntryAndProof")
   105  )
   106  
   107  var (
   108  	// Metrics are all per-log (label "logid"), but may also be
   109  	// per-entrypoint (label "ep") or per-return-code (label "rc").
   110  	once                       sync.Once
   111  	knownLogs                  monitoring.Gauge     // logid => value (always 1.0)
   112  	isMirrorLog                monitoring.Gauge     // logid => value (either 0.0 or 1.0)
   113  	maxMergeDelay              monitoring.Gauge     // logid => value
   114  	expMergeDelay              monitoring.Gauge     // logid => value
   115  	lastSCTTimestamp           monitoring.Gauge     // logid => value
   116  	lastSTHTimestamp           monitoring.Gauge     // logid => value
   117  	lastSTHTreeSize            monitoring.Gauge     // logid => value
   118  	frozenSTHTimestamp         monitoring.Gauge     // logid => value
   119  	reqsCounter                monitoring.Counter   // logid, ep => value
   120  	rspsCounter                monitoring.Counter   // logid, ep, rc => value
   121  	rspLatency                 monitoring.Histogram // logid, ep, rc => value
   122  	alignedGetEntries          monitoring.Counter   // logid, aligned => count
   123  	getEntriesStartPercentiles monitoring.Histogram // logid => percentile
   124  )
   125  
   126  // setupMetrics initializes all the exported metrics.
   127  func setupMetrics(mf monitoring.MetricFactory) {
   128  	knownLogs = mf.NewGauge("known_logs", "Set to 1 for known logs", "logid")
   129  	isMirrorLog = mf.NewGauge("is_mirror", "Set to 1 for mirror logs", "logid")
   130  	maxMergeDelay = mf.NewGauge("max_merge_delay", "Maximum Merge Delay in seconds", "logid")
   131  	expMergeDelay = mf.NewGauge("expected_merge_delay", "Expected Merge Delay in seconds", "logid")
   132  	lastSCTTimestamp = mf.NewGauge("last_sct_timestamp", "Time of last SCT in ms since epoch", "logid")
   133  	lastSTHTimestamp = mf.NewGauge("last_sth_timestamp", "Time of last STH in ms since epoch", "logid")
   134  	lastSTHTreeSize = mf.NewGauge("last_sth_treesize", "Size of tree at last STH", "logid")
   135  	frozenSTHTimestamp = mf.NewGauge("frozen_sth_timestamp", "Time of the frozen STH in ms since epoch", "logid")
   136  	reqsCounter = mf.NewCounter("http_reqs", "Number of requests", "logid", "ep")
   137  	rspsCounter = mf.NewCounter("http_rsps", "Number of responses", "logid", "ep", "rc")
   138  	rspLatency = mf.NewHistogram("http_latency", "Latency of responses in seconds", "logid", "ep", "rc")
   139  	alignedGetEntries = mf.NewCounter("aligned_get_entries", "Number of get-entries requests which were aligned to size limit boundaries", "logid", "aligned")
   140  	getEntriesStartPercentiles = mf.NewHistogramWithBuckets(
   141  		"get_leaves_start_percentiles",
   142  		"Start index of GetLeavesByRange request using percentage of current log size at the time",
   143  		monitoring.PercentileBuckets(5),
   144  		"logid",
   145  	)
   146  }
   147  
   148  // Entrypoints is a list of entrypoint names as exposed in statistics/logging.
   149  var Entrypoints = []EntrypointName{AddChainName, AddPreChainName, GetSTHName, GetSTHConsistencyName, GetProofByHashName, GetEntriesName, GetRootsName, GetEntryAndProofName}
   150  
   151  // PathHandlers maps from a path to the relevant AppHandler instance.
   152  type PathHandlers map[string]AppHandler
   153  
   154  // AppHandler holds a logInfo and a handler function that uses it, and is
   155  // an implementation of the http.Handler interface.
   156  type AppHandler struct {
   157  	Info    *logInfo
   158  	Handler func(context.Context, *logInfo, http.ResponseWriter, *http.Request) (int, error)
   159  	Name    EntrypointName
   160  	Method  string // http.MethodGet or http.MethodPost
   161  }
   162  
   163  // ServeHTTP for an AppHandler invokes the underlying handler function but
   164  // does additional common error and stats processing.
   165  func (a AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   166  	var statusCode int
   167  	label0 := strconv.FormatInt(a.Info.logID, 10)
   168  	label1 := string(a.Name)
   169  	reqsCounter.Inc(label0, label1)
   170  	startTime := a.Info.TimeSource.Now()
   171  	logCtx := a.Info.RequestLog.Start(r.Context())
   172  	a.Info.RequestLog.LogPrefix(logCtx, a.Info.LogPrefix)
   173  	defer func() {
   174  		latency := a.Info.TimeSource.Now().Sub(startTime).Seconds()
   175  		rspLatency.Observe(latency, label0, label1, strconv.Itoa(statusCode))
   176  	}()
   177  	klog.V(2).Infof("%s: request %v %q => %s", a.Info.LogPrefix, r.Method, r.URL, a.Name)
   178  	if r.Method != a.Method {
   179  		klog.Warningf("%s: %s wrong HTTP method: %v", a.Info.LogPrefix, a.Name, r.Method)
   180  		a.Info.SendHTTPError(w, http.StatusMethodNotAllowed, fmt.Errorf("method not allowed: %s", r.Method))
   181  		a.Info.RequestLog.Status(logCtx, http.StatusMethodNotAllowed)
   182  		return
   183  	}
   184  
   185  	// For GET requests all params come as form encoded so we might as well parse them now.
   186  	// POSTs will decode the raw request body as JSON later.
   187  	if r.Method == http.MethodGet {
   188  		if err := r.ParseForm(); err != nil {
   189  			a.Info.SendHTTPError(w, http.StatusBadRequest, fmt.Errorf("failed to parse form data: %s", err))
   190  			a.Info.RequestLog.Status(logCtx, http.StatusBadRequest)
   191  			return
   192  		}
   193  	}
   194  
   195  	// Many/most of the handlers forward the request on to the Log RPC server; impose a deadline
   196  	// on this onward request.
   197  	ctx, cancel := context.WithDeadline(logCtx, getRPCDeadlineTime(a.Info))
   198  	defer cancel()
   199  
   200  	var err error
   201  	statusCode, err = a.Handler(ctx, a.Info, w, r)
   202  	a.Info.RequestLog.Status(ctx, statusCode)
   203  	klog.V(2).Infof("%s: %s <= st=%d", a.Info.LogPrefix, a.Name, statusCode)
   204  	rspsCounter.Inc(label0, label1, strconv.Itoa(statusCode))
   205  	if err != nil {
   206  		klog.Warningf("%s: %s handler error: %v", a.Info.LogPrefix, a.Name, err)
   207  		a.Info.SendHTTPError(w, statusCode, err)
   208  		return
   209  	}
   210  
   211  	// Additional check, for consistency the handler must return an error for non-200 st
   212  	if statusCode != http.StatusOK {
   213  		klog.Warningf("%s: %s handler non 200 without error: %d %v", a.Info.LogPrefix, a.Name, statusCode, err)
   214  		a.Info.SendHTTPError(w, http.StatusInternalServerError, fmt.Errorf("http handler misbehaved, st: %d", statusCode))
   215  		return
   216  	}
   217  }
   218  
   219  // CertValidationOpts contains various parameters for certificate chain validation
   220  type CertValidationOpts struct {
   221  	// trustedRoots is a pool of certificates that defines the roots the CT log will accept
   222  	trustedRoots *x509util.PEMCertPool
   223  	// currentTime is the time used for checking a certificate's validity period
   224  	// against. If it's zero then time.Now() is used. Only for testing.
   225  	currentTime time.Time
   226  	// rejectExpired indicates that expired certificates will be rejected.
   227  	rejectExpired bool
   228  	// rejectUnexpired indicates that certificates that are currently valid or not yet valid will be rejected.
   229  	rejectUnexpired bool
   230  	// notAfterStart is the earliest notAfter date which will be accepted.
   231  	// nil means no lower bound on the accepted range.
   232  	notAfterStart *time.Time
   233  	// notAfterLimit defines the cut off point of notAfter dates - only notAfter
   234  	// dates strictly *before* notAfterLimit will be accepted.
   235  	// nil means no upper bound on the accepted range.
   236  	notAfterLimit *time.Time
   237  	// acceptOnlyCA will reject any certificate without the CA bit set.
   238  	acceptOnlyCA bool
   239  	// extKeyUsages contains the list of EKUs to use during chain verification
   240  	extKeyUsages []x509.ExtKeyUsage
   241  	// rejectExtIds contains a list of X.509 extension IDs to reject during chain verification.
   242  	rejectExtIds []asn1.ObjectIdentifier
   243  }
   244  
   245  // NewCertValidationOpts builds validation options based on parameters.
   246  func NewCertValidationOpts(trustedRoots *x509util.PEMCertPool, currentTime time.Time, rejectExpired bool, rejectUnexpired bool, notAfterStart *time.Time, notAfterLimit *time.Time, acceptOnlyCA bool, extKeyUsages []x509.ExtKeyUsage) CertValidationOpts {
   247  	var vOpts CertValidationOpts
   248  	vOpts.trustedRoots = trustedRoots
   249  	vOpts.currentTime = currentTime
   250  	vOpts.rejectExpired = rejectExpired
   251  	vOpts.rejectUnexpired = rejectUnexpired
   252  	vOpts.notAfterStart = notAfterStart
   253  	vOpts.notAfterLimit = notAfterLimit
   254  	vOpts.acceptOnlyCA = acceptOnlyCA
   255  	vOpts.extKeyUsages = extKeyUsages
   256  	return vOpts
   257  }
   258  
   259  // logInfo holds information for a specific log instance.
   260  type logInfo struct {
   261  	// LogPrefix is a pre-formatted string identifying the log for diagnostics
   262  	LogPrefix string
   263  	// TimeSource is a util.TimeSource that can be injected for testing
   264  	TimeSource util.TimeSource
   265  	// RequestLog is a logger for various request / processing / response debug
   266  	// information.
   267  	RequestLog RequestLog
   268  
   269  	// Instance-wide options
   270  	instanceOpts InstanceOptions
   271  	// logID is the tree ID that identifies this log in node storage
   272  	logID int64
   273  	// validationOpts contains the certificate chain validation parameters
   274  	validationOpts CertValidationOpts
   275  	// rpcClient is the client used to communicate with the Trillian backend
   276  	rpcClient trillian.TrillianLogClient
   277  	// signer signs objects (e.g. STHs, SCTs) for regular logs
   278  	signer crypto.Signer
   279  	// sthGetter provides STHs for the log
   280  	sthGetter STHGetter
   281  }
   282  
   283  // newLogInfo creates a new instance of logInfo.
   284  func newLogInfo(
   285  	instanceOpts InstanceOptions,
   286  	validationOpts CertValidationOpts,
   287  	signer crypto.Signer,
   288  	timeSource util.TimeSource,
   289  ) *logInfo {
   290  	vCfg := instanceOpts.Validated
   291  	cfg := vCfg.Config
   292  
   293  	logID, prefix := cfg.LogId, cfg.Prefix
   294  	li := &logInfo{
   295  		logID:          logID,
   296  		LogPrefix:      fmt.Sprintf("%s{%d}", prefix, logID),
   297  		rpcClient:      instanceOpts.Client,
   298  		signer:         signer,
   299  		TimeSource:     timeSource,
   300  		instanceOpts:   instanceOpts,
   301  		validationOpts: validationOpts,
   302  		RequestLog:     instanceOpts.RequestLog,
   303  	}
   304  
   305  	once.Do(func() { setupMetrics(instanceOpts.MetricFactory) })
   306  	label := strconv.FormatInt(logID, 10)
   307  	knownLogs.Set(1.0, label)
   308  
   309  	switch {
   310  	case vCfg.FrozenSTH != nil:
   311  		li.sthGetter = &FrozenSTHGetter{sth: vCfg.FrozenSTH}
   312  		frozenSTHTimestamp.Set(float64(vCfg.FrozenSTH.Timestamp), label)
   313  
   314  	case cfg.IsMirror:
   315  		st := instanceOpts.STHStorage
   316  		if st == nil {
   317  			st = DefaultMirrorSTHStorage{}
   318  		}
   319  		li.sthGetter = &MirrorSTHGetter{li: li, st: st}
   320  
   321  	default:
   322  		li.sthGetter = &LogSTHGetter{li: li}
   323  	}
   324  
   325  	if cfg.IsMirror {
   326  		isMirrorLog.Set(1.0, label)
   327  	} else {
   328  		isMirrorLog.Set(0.0, label)
   329  	}
   330  	maxMergeDelay.Set(float64(cfg.MaxMergeDelaySec), label)
   331  	expMergeDelay.Set(float64(cfg.ExpectedMergeDelaySec), label)
   332  
   333  	return li
   334  }
   335  
   336  // Handlers returns a map from URL paths (with the given prefix) and AppHandler instances
   337  // to handle those entrypoints.
   338  func (li *logInfo) Handlers(prefix string) PathHandlers {
   339  	if !strings.HasPrefix(prefix, "/") {
   340  		prefix = "/" + prefix
   341  	}
   342  	prefix = strings.TrimRight(prefix, "/")
   343  
   344  	// Bind the logInfo instance to give an AppHandler instance for each endpoint.
   345  	ph := PathHandlers{
   346  		prefix + ct.AddChainPath:          AppHandler{Info: li, Handler: addChain, Name: AddChainName, Method: http.MethodPost},
   347  		prefix + ct.AddPreChainPath:       AppHandler{Info: li, Handler: addPreChain, Name: AddPreChainName, Method: http.MethodPost},
   348  		prefix + ct.GetSTHPath:            AppHandler{Info: li, Handler: getSTH, Name: GetSTHName, Method: http.MethodGet},
   349  		prefix + ct.GetSTHConsistencyPath: AppHandler{Info: li, Handler: getSTHConsistency, Name: GetSTHConsistencyName, Method: http.MethodGet},
   350  		prefix + ct.GetProofByHashPath:    AppHandler{Info: li, Handler: getProofByHash, Name: GetProofByHashName, Method: http.MethodGet},
   351  		prefix + ct.GetEntriesPath:        AppHandler{Info: li, Handler: getEntries, Name: GetEntriesName, Method: http.MethodGet},
   352  		prefix + ct.GetRootsPath:          AppHandler{Info: li, Handler: getRoots, Name: GetRootsName, Method: http.MethodGet},
   353  		prefix + ct.GetEntryAndProofPath:  AppHandler{Info: li, Handler: getEntryAndProof, Name: GetEntryAndProofName, Method: http.MethodGet},
   354  	}
   355  	// Remove endpoints not provided by readonly logs and mirrors.
   356  	if li.instanceOpts.Validated.Config.IsReadonly || li.instanceOpts.Validated.Config.IsMirror {
   357  		delete(ph, prefix+ct.AddChainPath)
   358  		delete(ph, prefix+ct.AddPreChainPath)
   359  	}
   360  
   361  	return ph
   362  }
   363  
   364  // SendHTTPError generates a custom error page to give more information on why something didn't work
   365  func (li *logInfo) SendHTTPError(w http.ResponseWriter, statusCode int, err error) {
   366  	errorBody := http.StatusText(statusCode)
   367  	if !li.instanceOpts.MaskInternalErrors || statusCode != http.StatusInternalServerError {
   368  		errorBody += fmt.Sprintf("\n%v", err)
   369  	}
   370  	http.Error(w, errorBody, statusCode)
   371  }
   372  
   373  // getSTH returns the current STH as known to the STH getter, and updates tree
   374  // size / timestamp metrics correspondingly.
   375  func (li *logInfo) getSTH(ctx context.Context) (*ct.SignedTreeHead, error) {
   376  	sth, err := li.sthGetter.GetSTH(ctx)
   377  	if err != nil {
   378  		return nil, err
   379  	}
   380  	logID := strconv.FormatInt(li.logID, 10)
   381  	lastSTHTimestamp.Set(float64(sth.Timestamp), logID)
   382  	lastSTHTreeSize.Set(float64(sth.TreeSize), logID)
   383  	return sth, nil
   384  }
   385  
   386  // ParseBodyAsJSONChain tries to extract cert-chain out of request.
   387  func ParseBodyAsJSONChain(r *http.Request) (ct.AddChainRequest, error) {
   388  	body, err := io.ReadAll(r.Body)
   389  	if err != nil {
   390  		klog.V(1).Infof("Failed to read request body: %v", err)
   391  		return ct.AddChainRequest{}, err
   392  	}
   393  
   394  	var req ct.AddChainRequest
   395  	if err := json.Unmarshal(body, &req); err != nil {
   396  		klog.V(1).Infof("Failed to parse request body: %v", err)
   397  		return ct.AddChainRequest{}, err
   398  	}
   399  
   400  	// The cert chain is not allowed to be empty. We'll defer other validation for later
   401  	if len(req.Chain) == 0 {
   402  		klog.V(1).Infof("Request chain is empty: %q", body)
   403  		return ct.AddChainRequest{}, errors.New("cert chain was empty")
   404  	}
   405  
   406  	return req, nil
   407  }
   408  
   409  // appendUserCharge adds the specified user to the passed in ChargeTo and
   410  // and returns the result.
   411  // If the passed-in ChargeTo is nil, then a new one is created with the passed
   412  // in user and returned.
   413  func appendUserCharge(a *trillian.ChargeTo, user string) *trillian.ChargeTo {
   414  	if a == nil {
   415  		a = &trillian.ChargeTo{}
   416  	}
   417  	a.User = append(a.User, user)
   418  	return a
   419  }
   420  
   421  // chargeUser returns a trillian.ChargeTo containing an ID for the remote User,
   422  // or nil if instanceOpts does not have a RemoteQuotaUser function set.
   423  func (li *logInfo) chargeUser(r *http.Request) *trillian.ChargeTo {
   424  	if li.instanceOpts.RemoteQuotaUser != nil {
   425  		return &trillian.ChargeTo{User: []string{li.instanceOpts.RemoteQuotaUser(r)}}
   426  	}
   427  	return nil
   428  }
   429  
   430  // addChainInternal is called by add-chain and add-pre-chain as the logic involved in
   431  // processing these requests is almost identical
   432  func addChainInternal(ctx context.Context, li *logInfo, w http.ResponseWriter, r *http.Request, isPrecert bool) (int, error) {
   433  	var method EntrypointName
   434  	var etype ct.LogEntryType
   435  	if isPrecert {
   436  		method = AddPreChainName
   437  		etype = ct.PrecertLogEntryType
   438  	} else {
   439  		method = AddChainName
   440  		etype = ct.X509LogEntryType
   441  	}
   442  
   443  	// Check the contents of the request and convert to slice of certificates.
   444  	addChainReq, err := ParseBodyAsJSONChain(r)
   445  	if err != nil {
   446  		return http.StatusBadRequest, fmt.Errorf("%s: failed to parse add-chain body: %s", li.LogPrefix, err)
   447  	}
   448  	// Log the DERs now because they might not parse as valid X.509.
   449  	for _, der := range addChainReq.Chain {
   450  		li.RequestLog.AddDERToChain(ctx, der)
   451  	}
   452  	chain, err := verifyAddChain(li, addChainReq, isPrecert)
   453  	if err != nil {
   454  		return http.StatusBadRequest, fmt.Errorf("failed to verify add-chain contents: %s", err)
   455  	}
   456  	for _, cert := range chain {
   457  		li.RequestLog.AddCertToChain(ctx, cert)
   458  	}
   459  	// Get the current time in the form used throughout RFC6962, namely milliseconds since Unix
   460  	// epoch, and use this throughout.
   461  	timeMillis := uint64(li.TimeSource.Now().UnixNano() / millisPerNano)
   462  
   463  	// Build the MerkleTreeLeaf that gets sent to the backend, and make a trillian.LogLeaf for it.
   464  	merkleLeaf, err := ct.MerkleTreeLeafFromChain(chain, etype, timeMillis)
   465  	if err != nil {
   466  		return http.StatusBadRequest, fmt.Errorf("failed to build MerkleTreeLeaf: %s", err)
   467  	}
   468  	leaf, err := buildLogLeafForAddChain(li, *merkleLeaf, chain, isPrecert)
   469  	if err != nil {
   470  		return http.StatusInternalServerError, fmt.Errorf("failed to build LogLeaf: %s", err)
   471  	}
   472  
   473  	// Send the Merkle tree leaf on to the Log server.
   474  	req := trillian.QueueLeafRequest{
   475  		LogId:    li.logID,
   476  		Leaf:     &leaf,
   477  		ChargeTo: li.chargeUser(r),
   478  	}
   479  	if li.instanceOpts.CertificateQuotaUser != nil {
   480  		// TODO(al): ignore pre-issuers? Probably doesn't matter
   481  		for _, cert := range chain[1:] {
   482  			req.ChargeTo = appendUserCharge(req.ChargeTo, li.instanceOpts.CertificateQuotaUser(cert))
   483  		}
   484  	}
   485  
   486  	klog.V(2).Infof("%s: %s => grpc.QueueLeaves", li.LogPrefix, method)
   487  	rsp, err := li.rpcClient.QueueLeaf(ctx, &req)
   488  	klog.V(2).Infof("%s: %s <= grpc.QueueLeaves err=%v", li.LogPrefix, method, err)
   489  	if err != nil {
   490  		return li.toHTTPStatus(err), fmt.Errorf("backend QueueLeaves request failed: %s", err)
   491  	}
   492  	if rsp == nil {
   493  		return http.StatusInternalServerError, errors.New("missing QueueLeaves response")
   494  	}
   495  	if rsp.QueuedLeaf == nil {
   496  		return http.StatusInternalServerError, errors.New("QueueLeaf did not return the leaf")
   497  	}
   498  
   499  	// Always use the returned leaf as the basis for an SCT.
   500  	var loggedLeaf ct.MerkleTreeLeaf
   501  	if rest, err := tls.Unmarshal(rsp.QueuedLeaf.Leaf.LeafValue, &loggedLeaf); err != nil {
   502  		return http.StatusInternalServerError, fmt.Errorf("failed to reconstruct MerkleTreeLeaf: %s", err)
   503  	} else if len(rest) > 0 {
   504  		return http.StatusInternalServerError, fmt.Errorf("extra data (%d bytes) on reconstructing MerkleTreeLeaf", len(rest))
   505  	}
   506  
   507  	// As the Log server has definitely got the Merkle tree leaf, we can
   508  	// generate an SCT and respond with it.
   509  	sct, err := buildV1SCT(li.signer, &loggedLeaf)
   510  	if err != nil {
   511  		return http.StatusInternalServerError, fmt.Errorf("failed to generate SCT: %s", err)
   512  	}
   513  	sctBytes, err := tls.Marshal(*sct)
   514  	if err != nil {
   515  		return http.StatusInternalServerError, fmt.Errorf("failed to marshall SCT: %s", err)
   516  	}
   517  	// We could possibly fail to issue the SCT after this but it's v. unlikely.
   518  	li.RequestLog.IssueSCT(ctx, sctBytes)
   519  	err = marshalAndWriteAddChainResponse(sct, li.signer, w)
   520  	if err != nil {
   521  		// reason is logged and http status is already set
   522  		return http.StatusInternalServerError, fmt.Errorf("failed to write response: %s", err)
   523  	}
   524  	klog.V(3).Infof("%s: %s <= SCT", li.LogPrefix, method)
   525  	if sct.Timestamp == timeMillis {
   526  		lastSCTTimestamp.Set(float64(sct.Timestamp), strconv.FormatInt(li.logID, 10))
   527  	}
   528  
   529  	return http.StatusOK, nil
   530  }
   531  
   532  func addChain(ctx context.Context, li *logInfo, w http.ResponseWriter, r *http.Request) (int, error) {
   533  	return addChainInternal(ctx, li, w, r, false)
   534  }
   535  
   536  func addPreChain(ctx context.Context, li *logInfo, w http.ResponseWriter, r *http.Request) (int, error) {
   537  	return addChainInternal(ctx, li, w, r, true)
   538  }
   539  
   540  func getSTH(ctx context.Context, li *logInfo, w http.ResponseWriter, r *http.Request) (int, error) {
   541  	qctx := ctx
   542  	if li.instanceOpts.RemoteQuotaUser != nil {
   543  		rqu := li.instanceOpts.RemoteQuotaUser(r)
   544  		qctx = context.WithValue(qctx, remoteQuotaCtxKey, rqu)
   545  	}
   546  	sth, err := li.getSTH(qctx)
   547  	if err != nil {
   548  		return li.toHTTPStatus(err), err
   549  	}
   550  	if err := writeSTH(sth, w); err != nil {
   551  		return http.StatusInternalServerError, err
   552  	}
   553  	return http.StatusOK, nil
   554  }
   555  
   556  // writeSTH marshals the STH to JSON and writes it to HTTP response.
   557  func writeSTH(sth *ct.SignedTreeHead, w http.ResponseWriter) error {
   558  	jsonRsp := ct.GetSTHResponse{
   559  		TreeSize:       sth.TreeSize,
   560  		SHA256RootHash: sth.SHA256RootHash[:],
   561  		Timestamp:      sth.Timestamp,
   562  	}
   563  	var err error
   564  	jsonRsp.TreeHeadSignature, err = tls.Marshal(sth.TreeHeadSignature)
   565  	if err != nil {
   566  		return fmt.Errorf("failed to tls.Marshal signature: %s", err)
   567  	}
   568  
   569  	w.Header().Set(contentTypeHeader, contentTypeJSON)
   570  	jsonData, err := json.Marshal(&jsonRsp)
   571  	if err != nil {
   572  		return fmt.Errorf("failed to marshal response: %s", err)
   573  	}
   574  
   575  	_, err = w.Write(jsonData)
   576  	if err != nil {
   577  		// Probably too late for this as headers might have been written but we
   578  		// don't know for sure.
   579  		return fmt.Errorf("failed to write response data: %s", err)
   580  	}
   581  
   582  	return nil
   583  }
   584  
   585  // nolint:staticcheck
   586  func getSTHConsistency(ctx context.Context, li *logInfo, w http.ResponseWriter, r *http.Request) (int, error) {
   587  	first, second, err := parseGetSTHConsistencyRange(r)
   588  	if err != nil {
   589  		return http.StatusBadRequest, fmt.Errorf("failed to parse consistency range: %s", err)
   590  	}
   591  	li.RequestLog.FirstAndSecond(ctx, first, second)
   592  	var jsonRsp ct.GetSTHConsistencyResponse
   593  	if first != 0 {
   594  		req := trillian.GetConsistencyProofRequest{
   595  			LogId:          li.logID,
   596  			FirstTreeSize:  first,
   597  			SecondTreeSize: second,
   598  			ChargeTo:       li.chargeUser(r),
   599  		}
   600  
   601  		klog.V(2).Infof("%s: GetSTHConsistency(%d, %d) => grpc.GetConsistencyProof %+v", li.LogPrefix, first, second, prototext.Format(&req))
   602  		rsp, err := li.rpcClient.GetConsistencyProof(ctx, &req)
   603  		klog.V(2).Infof("%s: GetSTHConsistency <= grpc.GetConsistencyProof err=%v", li.LogPrefix, err)
   604  		if err != nil {
   605  			return li.toHTTPStatus(err), fmt.Errorf("backend GetConsistencyProof request failed: %s", err)
   606  		}
   607  
   608  		var currentRoot types.LogRootV1
   609  		if err := currentRoot.UnmarshalBinary(rsp.GetSignedLogRoot().GetLogRoot()); err != nil {
   610  			return http.StatusInternalServerError, fmt.Errorf("failed to unmarshal root: %v", rsp.GetSignedLogRoot().GetLogRoot())
   611  		}
   612  		// We can get here with a tree size too small to satisfy the proof.
   613  		if currentRoot.TreeSize < uint64(second) {
   614  			return http.StatusBadRequest, fmt.Errorf("need tree size: %d for proof but only got: %d", second, currentRoot.TreeSize)
   615  		}
   616  
   617  		// Additional sanity checks, none of the hashes in the returned path should be empty
   618  		if !checkAuditPath(rsp.Proof.Hashes) {
   619  			return http.StatusInternalServerError, fmt.Errorf("backend returned invalid proof: %v", rsp.Proof)
   620  		}
   621  
   622  		// We got a valid response from the server. Marshal it as JSON and return it to the client
   623  		jsonRsp.Consistency = rsp.Proof.Hashes
   624  		if jsonRsp.Consistency == nil {
   625  			jsonRsp.Consistency = emptyProof
   626  		}
   627  	} else {
   628  		klog.V(2).Infof("%s: GetSTHConsistency(%d, %d) starts from 0 so return empty proof", li.LogPrefix, first, second)
   629  		jsonRsp.Consistency = emptyProof
   630  	}
   631  
   632  	w.Header().Set(cacheControlHeader, cacheControlImmutable)
   633  	w.Header().Set(contentTypeHeader, contentTypeJSON)
   634  	jsonData, err := json.Marshal(&jsonRsp)
   635  	if err != nil {
   636  		return http.StatusInternalServerError, fmt.Errorf("failed to marshal get-sth-consistency resp: %s", err)
   637  	}
   638  
   639  	_, err = w.Write(jsonData)
   640  	if err != nil {
   641  		// Probably too late for this as headers might have been written but we don't know for sure
   642  		return http.StatusInternalServerError, fmt.Errorf("failed to write get-sth-consistency resp: %s", err)
   643  	}
   644  
   645  	return http.StatusOK, nil
   646  }
   647  
   648  // nolint:staticcheck
   649  func getProofByHash(ctx context.Context, li *logInfo, w http.ResponseWriter, r *http.Request) (int, error) {
   650  	// Accept any non empty hash that decodes from base64 and let the backend validate it further
   651  	hash := r.FormValue(getProofParamHash)
   652  	if len(hash) == 0 {
   653  		return http.StatusBadRequest, errors.New("get-proof-by-hash: missing / empty hash param for get-proof-by-hash")
   654  	}
   655  	leafHash, err := base64.StdEncoding.DecodeString(hash)
   656  	if err != nil {
   657  		return http.StatusBadRequest, fmt.Errorf("get-proof-by-hash: invalid base64 hash: %s", err)
   658  	}
   659  
   660  	treeSize, err := strconv.ParseInt(r.FormValue(getProofParamTreeSize), 10, 64)
   661  	if err != nil || treeSize < 1 {
   662  		return http.StatusBadRequest, fmt.Errorf("get-proof-by-hash: missing or invalid tree_size: %v", r.FormValue(getProofParamTreeSize))
   663  	}
   664  	li.RequestLog.LeafHash(ctx, leafHash)
   665  	li.RequestLog.TreeSize(ctx, treeSize)
   666  
   667  	// Per RFC 6962 section 4.5 the API returns a single proof. This should be the lowest leaf index
   668  	// Because we request order by sequence and we only passed one hash then the first result is
   669  	// the correct proof to return
   670  	req := trillian.GetInclusionProofByHashRequest{
   671  		LogId:           li.logID,
   672  		LeafHash:        leafHash,
   673  		TreeSize:        treeSize,
   674  		OrderBySequence: true,
   675  		ChargeTo:        li.chargeUser(r),
   676  	}
   677  	rsp, err := li.rpcClient.GetInclusionProofByHash(ctx, &req)
   678  	if err != nil {
   679  		return li.toHTTPStatus(err), fmt.Errorf("backend GetInclusionProofByHash request failed: %s", err)
   680  	}
   681  
   682  	var currentRoot types.LogRootV1
   683  	if err := currentRoot.UnmarshalBinary(rsp.GetSignedLogRoot().GetLogRoot()); err != nil {
   684  		return http.StatusInternalServerError, fmt.Errorf("failed to unmarshal root: %v", rsp.GetSignedLogRoot().GetLogRoot())
   685  	}
   686  	// We could fail to get the proof because the tree size that the server has
   687  	// is not large enough.
   688  	if currentRoot.TreeSize < uint64(treeSize) {
   689  		return http.StatusNotFound, fmt.Errorf("log returned tree size: %d but we expected: %d", currentRoot.TreeSize, treeSize)
   690  	}
   691  
   692  	// Additional sanity checks on the response.
   693  	if len(rsp.Proof) == 0 {
   694  		// The backend returns the STH even when there is no proof, so explicitly
   695  		// map this to 4xx.
   696  		return http.StatusNotFound, errors.New("get-proof-by-hash: backend did not return a proof")
   697  	}
   698  	if !checkAuditPath(rsp.Proof[0].Hashes) {
   699  		return http.StatusInternalServerError, fmt.Errorf("get-proof-by-hash: backend returned invalid proof: %v", rsp.Proof[0])
   700  	}
   701  
   702  	// All checks complete, marshal and return the response
   703  	proofRsp := ct.GetProofByHashResponse{
   704  		LeafIndex: rsp.Proof[0].LeafIndex,
   705  		AuditPath: rsp.Proof[0].Hashes,
   706  	}
   707  	if proofRsp.AuditPath == nil {
   708  		proofRsp.AuditPath = emptyProof
   709  	}
   710  
   711  	w.Header().Set(cacheControlHeader, cacheControlImmutable)
   712  	w.Header().Set(contentTypeHeader, contentTypeJSON)
   713  	jsonData, err := json.Marshal(&proofRsp)
   714  	if err != nil {
   715  		klog.Warningf("%s: Failed to marshal get-proof-by-hash resp: %v", li.LogPrefix, proofRsp)
   716  		return http.StatusInternalServerError, fmt.Errorf("failed to marshal get-proof-by-hash resp: %s", err)
   717  	}
   718  
   719  	_, err = w.Write(jsonData)
   720  	if err != nil {
   721  		// Probably too late for this as headers might have been written but we don't know for sure
   722  		return http.StatusInternalServerError, fmt.Errorf("failed to write get-proof-by-hash resp: %s", err)
   723  	}
   724  
   725  	return http.StatusOK, nil
   726  }
   727  
   728  // nolint:staticcheck
   729  func getEntries(ctx context.Context, li *logInfo, w http.ResponseWriter, r *http.Request) (int, error) {
   730  	// The first job is to parse the params and make sure they're sensible. We just make
   731  	// sure the range is valid. We don't do an extra roundtrip to get the current tree
   732  	// size and prefer to let the backend handle this case
   733  	start, end, err := parseGetEntriesRange(r, MaxGetEntriesAllowed, li.logID)
   734  	if err != nil {
   735  		return http.StatusBadRequest, fmt.Errorf("bad range on get-entries request: %s", err)
   736  	}
   737  	li.RequestLog.StartAndEnd(ctx, start, end)
   738  
   739  	// Now make a request to the backend to get the relevant leaves
   740  	var leaves []*trillian.LogLeaf
   741  	count := end + 1 - start
   742  	req := trillian.GetLeavesByRangeRequest{
   743  		LogId:      li.logID,
   744  		StartIndex: start,
   745  		Count:      count,
   746  		ChargeTo:   li.chargeUser(r),
   747  	}
   748  	rsp, err := li.rpcClient.GetLeavesByRange(ctx, &req)
   749  	if err != nil {
   750  		return li.toHTTPStatus(err), fmt.Errorf("backend GetLeavesByRange request failed: %s", err)
   751  	}
   752  	var currentRoot types.LogRootV1
   753  	if err := currentRoot.UnmarshalBinary(rsp.GetSignedLogRoot().GetLogRoot()); err != nil {
   754  		return http.StatusInternalServerError, fmt.Errorf("failed to unmarshal root: %v", rsp.GetSignedLogRoot().GetLogRoot())
   755  	}
   756  	if currentRoot.TreeSize <= uint64(start) {
   757  		// If the returned tree is too small to contain any leaves return the 4xx
   758  		// explicitly here.
   759  		return http.StatusBadRequest, fmt.Errorf("need tree size: %d to get leaves but only got: %d", start+1, currentRoot.TreeSize)
   760  	}
   761  	if *getEntriesMetrics {
   762  		label := strconv.FormatInt(req.LogId, 10)
   763  		recordStartPercent(start, currentRoot.TreeSize, label)
   764  	}
   765  	// Do some sanity checks on the result.
   766  	if len(rsp.Leaves) > int(count) {
   767  		return http.StatusInternalServerError, fmt.Errorf("backend returned too many leaves: %d vs [%d,%d]", len(rsp.Leaves), start, end)
   768  	}
   769  	for i, leaf := range rsp.Leaves {
   770  		if leaf.LeafIndex != start+int64(i) {
   771  			return http.StatusInternalServerError, fmt.Errorf("backend returned unexpected leaf index: rsp.Leaves[%d].LeafIndex=%d for range [%d,%d]", i, leaf.LeafIndex, start, end)
   772  		}
   773  	}
   774  	leaves = rsp.Leaves
   775  
   776  	// Now we've checked the RPC response and it seems to be valid we need
   777  	// to serialize the leaves in JSON format for the HTTP response. Doing a
   778  	// round trip via the leaf deserializer gives us another chance to
   779  	// prevent bad / corrupt data from reaching the client.
   780  	jsonRsp, err := marshalGetEntriesResponse(li, leaves)
   781  	if err != nil {
   782  		return http.StatusInternalServerError, fmt.Errorf("failed to process leaves returned from backend: %s", err)
   783  	}
   784  
   785  	w.Header().Set(cacheControlHeader, cacheControlImmutable)
   786  	w.Header().Set(contentTypeHeader, contentTypeJSON)
   787  	jsonData, err := json.Marshal(&jsonRsp)
   788  	if err != nil {
   789  		return http.StatusInternalServerError, fmt.Errorf("failed to marshal get-entries resp: %s", err)
   790  	}
   791  
   792  	_, err = w.Write(jsonData)
   793  	if err != nil {
   794  		// Probably too late for this as headers might have been written but we don't know for sure
   795  		return http.StatusInternalServerError, fmt.Errorf("failed to write get-entries resp: %s", err)
   796  	}
   797  
   798  	return http.StatusOK, nil
   799  }
   800  
   801  func getRoots(_ context.Context, li *logInfo, w http.ResponseWriter, _ *http.Request) (int, error) {
   802  	// Pull out the raw certificates from the parsed versions
   803  	rawCerts := make([][]byte, 0, len(li.validationOpts.trustedRoots.RawCertificates()))
   804  	for _, cert := range li.validationOpts.trustedRoots.RawCertificates() {
   805  		rawCerts = append(rawCerts, cert.Raw)
   806  	}
   807  
   808  	jsonMap := make(map[string]interface{})
   809  	jsonMap[jsonMapKeyCertificates] = rawCerts
   810  	enc := json.NewEncoder(w)
   811  	err := enc.Encode(jsonMap)
   812  	if err != nil {
   813  		klog.Warningf("%s: get_roots failed: %v", li.LogPrefix, err)
   814  		return http.StatusInternalServerError, fmt.Errorf("get-roots failed with: %s", err)
   815  	}
   816  
   817  	return http.StatusOK, nil
   818  }
   819  
   820  // See RFC 6962 Section 4.8.
   821  // nolint:staticcheck
   822  func getEntryAndProof(ctx context.Context, li *logInfo, w http.ResponseWriter, r *http.Request) (int, error) {
   823  	// Ensure both numeric params are present and look reasonable.
   824  	leafIndex, treeSize, err := parseGetEntryAndProofParams(r)
   825  	if err != nil {
   826  		return http.StatusBadRequest, fmt.Errorf("failed to parse get-entry-and-proof params: %s", err)
   827  	}
   828  	li.RequestLog.LeafIndex(ctx, leafIndex)
   829  	li.RequestLog.TreeSize(ctx, treeSize)
   830  
   831  	req := trillian.GetEntryAndProofRequest{
   832  		LogId:     li.logID,
   833  		LeafIndex: leafIndex,
   834  		TreeSize:  treeSize,
   835  		ChargeTo:  li.chargeUser(r),
   836  	}
   837  	rsp, err := li.rpcClient.GetEntryAndProof(ctx, &req)
   838  	if err != nil {
   839  		return li.toHTTPStatus(err), fmt.Errorf("backend GetEntryAndProof request failed: %s", err)
   840  	}
   841  
   842  	var currentRoot types.LogRootV1
   843  	if err := currentRoot.UnmarshalBinary(rsp.GetSignedLogRoot().GetLogRoot()); err != nil {
   844  		return http.StatusInternalServerError, fmt.Errorf("failed to unmarshal root: %v", rsp.GetSignedLogRoot().GetLogRoot())
   845  	}
   846  	if currentRoot.TreeSize < uint64(treeSize) {
   847  		// If tree size is not large enough return the 4xx here, would previously
   848  		// have come from the error status mapping above.
   849  		return http.StatusBadRequest, fmt.Errorf("need tree size: %d for proof but only got: %d", req.TreeSize, currentRoot.TreeSize)
   850  	}
   851  
   852  	// Apply some checks that we got reasonable data from the backend
   853  	if rsp.Leaf == nil || len(rsp.Leaf.LeafValue) == 0 || rsp.Proof == nil {
   854  		return http.StatusInternalServerError, fmt.Errorf("got RPC bad response, possible extra info: %v", rsp)
   855  	}
   856  	if treeSize > 1 && len(rsp.Proof.Hashes) == 0 {
   857  		return http.StatusInternalServerError, fmt.Errorf("got RPC bad response (missing proof), possible extra info: %v", rsp)
   858  	}
   859  
   860  	// Build and marshal the response to the client
   861  	jsonRsp := ct.GetEntryAndProofResponse{
   862  		LeafInput: rsp.Leaf.LeafValue,
   863  		ExtraData: rsp.Leaf.ExtraData,
   864  		AuditPath: rsp.Proof.Hashes,
   865  	}
   866  
   867  	w.Header().Set(cacheControlHeader, cacheControlImmutable)
   868  	w.Header().Set(contentTypeHeader, contentTypeJSON)
   869  	jsonData, err := json.Marshal(&jsonRsp)
   870  	if err != nil {
   871  		return http.StatusInternalServerError, fmt.Errorf("failed to marshal get-entry-and-proof resp: %s", err)
   872  	}
   873  
   874  	_, err = w.Write(jsonData)
   875  	if err != nil {
   876  
   877  		// Probably too late for this as headers might have been written but we don't know for sure
   878  		return http.StatusInternalServerError, fmt.Errorf("failed to write get-entry-and-proof resp: %s", err)
   879  	}
   880  
   881  	return http.StatusOK, nil
   882  }
   883  
   884  // getRPCDeadlineTime calculates the future time an RPC should expire based on our config
   885  func getRPCDeadlineTime(li *logInfo) time.Time {
   886  	return li.TimeSource.Now().Add(li.instanceOpts.Deadline)
   887  }
   888  
   889  // verifyAddChain is used by add-chain and add-pre-chain. It does the checks that the supplied
   890  // cert is of the correct type and chains to a trusted root.
   891  func verifyAddChain(li *logInfo, req ct.AddChainRequest, expectingPrecert bool) ([]*x509.Certificate, error) {
   892  	// We already checked that the chain is not empty so can move on to verification
   893  	validPath, err := ValidateChain(req.Chain, li.validationOpts)
   894  	if err != nil {
   895  		// We rejected it because the cert failed checks or we could not find a path to a root etc.
   896  		// Lots of possible causes for errors
   897  		return nil, fmt.Errorf("chain failed to verify: %s", err)
   898  	}
   899  
   900  	isPrecert, err := IsPrecertificate(validPath[0])
   901  	if err != nil {
   902  		return nil, fmt.Errorf("precert test failed: %s", err)
   903  	}
   904  
   905  	// The type of the leaf must match the one the handler expects
   906  	if isPrecert != expectingPrecert {
   907  		if expectingPrecert {
   908  			klog.Warningf("%s: Cert (or precert with invalid CT ext) submitted as precert chain: %q", li.LogPrefix, req.Chain)
   909  		} else {
   910  			klog.Warningf("%s: Precert (or cert with invalid CT ext) submitted as cert chain: %q", li.LogPrefix, req.Chain)
   911  		}
   912  		return nil, fmt.Errorf("cert / precert mismatch: %T", expectingPrecert)
   913  	}
   914  
   915  	return validPath, nil
   916  }
   917  
   918  func extractRawCerts(chain []*x509.Certificate) []ct.ASN1Cert {
   919  	raw := make([]ct.ASN1Cert, len(chain))
   920  	for i, cert := range chain {
   921  		raw[i] = ct.ASN1Cert{Data: cert.Raw}
   922  	}
   923  	return raw
   924  }
   925  
   926  // buildLogLeafForAddChain does the hashing to build a LogLeaf that will be
   927  // sent to the backend by add-chain and add-pre-chain endpoints.
   928  func buildLogLeafForAddChain(li *logInfo,
   929  	merkleLeaf ct.MerkleTreeLeaf, chain []*x509.Certificate, isPrecert bool,
   930  ) (trillian.LogLeaf, error) {
   931  	raw := extractRawCerts(chain)
   932  	return util.BuildLogLeaf(li.LogPrefix, merkleLeaf, 0, raw[0], raw[1:], isPrecert)
   933  }
   934  
   935  // marshalAndWriteAddChainResponse is used by add-chain and add-pre-chain to create and write
   936  // the JSON response to the client
   937  func marshalAndWriteAddChainResponse(sct *ct.SignedCertificateTimestamp, signer crypto.Signer, w http.ResponseWriter) error {
   938  	logID, err := GetCTLogID(signer.Public())
   939  	if err != nil {
   940  		return fmt.Errorf("failed to marshal logID: %s", err)
   941  	}
   942  	sig, err := tls.Marshal(sct.Signature)
   943  	if err != nil {
   944  		return fmt.Errorf("failed to marshal signature: %s", err)
   945  	}
   946  
   947  	rsp := ct.AddChainResponse{
   948  		SCTVersion: sct.SCTVersion,
   949  		Timestamp:  sct.Timestamp,
   950  		ID:         logID[:],
   951  		Extensions: base64.StdEncoding.EncodeToString(sct.Extensions),
   952  		Signature:  sig,
   953  	}
   954  
   955  	w.Header().Set(contentTypeHeader, contentTypeJSON)
   956  	jsonData, err := json.Marshal(&rsp)
   957  	if err != nil {
   958  		return fmt.Errorf("failed to marshal add-chain: %s", err)
   959  	}
   960  
   961  	_, err = w.Write(jsonData)
   962  	if err != nil {
   963  		return fmt.Errorf("failed to write add-chain resp: %s", err)
   964  	}
   965  
   966  	return nil
   967  }
   968  
   969  func parseGetEntriesRange(r *http.Request, maxRange, logID int64) (int64, int64, error) {
   970  	start, err := strconv.ParseInt(r.FormValue(getEntriesParamStart), 10, 64)
   971  	if err != nil {
   972  		return 0, 0, err
   973  	}
   974  
   975  	end, err := strconv.ParseInt(r.FormValue(getEntriesParamEnd), 10, 64)
   976  	if err != nil {
   977  		return 0, 0, err
   978  	}
   979  
   980  	if start < 0 || end < 0 {
   981  		return 0, 0, fmt.Errorf("start (%d) and end (%d) parameters must be >= 0", start, end)
   982  	}
   983  	if start > end {
   984  		return 0, 0, fmt.Errorf("start (%d) and end (%d) is not a valid range", start, end)
   985  	}
   986  
   987  	count := end - start + 1
   988  	if count > maxRange {
   989  		end = start + maxRange - 1
   990  	}
   991  	if *alignGetEntries && count >= maxRange {
   992  		// Truncate a "maximally sized" get-entries request at the next multiple
   993  		// of MaxGetEntriesAllowed.
   994  		// This is intended to coerce large runs of get-entries requests (e.g. by
   995  		// monitors/mirrors) into all requesting the same start/end ranges,
   996  		// thereby making the responses more readily cacheable.
   997  		d := (end + 1) % maxRange
   998  		end = end - d
   999  		alignedGetEntries.Inc(strconv.FormatInt(logID, 10), strconv.FormatBool(d == 0))
  1000  	}
  1001  
  1002  	return start, end, nil
  1003  }
  1004  
  1005  func parseGetEntryAndProofParams(r *http.Request) (int64, int64, error) {
  1006  	leafIndex, err := strconv.ParseInt(r.FormValue(getEntryAndProofParamLeafIndex), 10, 64)
  1007  	if err != nil {
  1008  		return 0, 0, err
  1009  	}
  1010  
  1011  	treeSize, err := strconv.ParseInt(r.FormValue(getEntryAndProofParamTreeSize), 10, 64)
  1012  	if err != nil {
  1013  		return 0, 0, err
  1014  	}
  1015  
  1016  	if treeSize <= 0 {
  1017  		return 0, 0, fmt.Errorf("tree_size must be > 0, got: %d", treeSize)
  1018  	}
  1019  	if leafIndex < 0 {
  1020  		return 0, 0, fmt.Errorf("leaf_index must be >= 0, got: %d", treeSize)
  1021  	}
  1022  	if leafIndex >= treeSize {
  1023  		return 0, 0, fmt.Errorf("leaf_index %d out of range for tree of size %d", leafIndex, treeSize)
  1024  	}
  1025  
  1026  	return leafIndex, treeSize, nil
  1027  }
  1028  
  1029  func parseGetSTHConsistencyRange(r *http.Request) (int64, int64, error) {
  1030  	firstVal := r.FormValue(getSTHConsistencyParamFirst)
  1031  	secondVal := r.FormValue(getSTHConsistencyParamSecond)
  1032  	if firstVal == "" {
  1033  		return 0, 0, errors.New("parameter 'first' is required")
  1034  	}
  1035  	if secondVal == "" {
  1036  		return 0, 0, errors.New("parameter 'second' is required")
  1037  	}
  1038  
  1039  	first, err := strconv.ParseInt(firstVal, 10, 64)
  1040  	if err != nil {
  1041  		return 0, 0, errors.New("parameter 'first' is malformed")
  1042  	}
  1043  
  1044  	second, err := strconv.ParseInt(secondVal, 10, 64)
  1045  	if err != nil {
  1046  		return 0, 0, errors.New("parameter 'second' is malformed")
  1047  	}
  1048  
  1049  	if first < 0 || second < 0 {
  1050  		return 0, 0, fmt.Errorf("first and second params cannot be <0: %d %d", first, second)
  1051  	}
  1052  	if second < first {
  1053  		return 0, 0, fmt.Errorf("invalid first, second params: %d %d", first, second)
  1054  	}
  1055  
  1056  	return first, second, nil
  1057  }
  1058  
  1059  // marshalGetEntriesResponse does the conversion from the backend response to the one we need for
  1060  // an RFC compliant JSON response to the client.
  1061  func marshalGetEntriesResponse(li *logInfo, leaves []*trillian.LogLeaf) (ct.GetEntriesResponse, error) {
  1062  	jsonRsp := ct.GetEntriesResponse{}
  1063  
  1064  	for _, leaf := range leaves {
  1065  		// We're only deserializing it to ensure it's valid, don't need the result. We still
  1066  		// return the data if it fails to deserialize as otherwise the root hash could not
  1067  		// be verified. However this indicates a potentially serious failure in log operation
  1068  		// or data storage that should be investigated.
  1069  		var treeLeaf ct.MerkleTreeLeaf
  1070  		if rest, err := tls.Unmarshal(leaf.LeafValue, &treeLeaf); err != nil {
  1071  			klog.Errorf("%s: Failed to deserialize Merkle leaf from backend: %d", li.LogPrefix, leaf.LeafIndex)
  1072  		} else if len(rest) > 0 {
  1073  			klog.Errorf("%s: Trailing data after Merkle leaf from backend: %d", li.LogPrefix, leaf.LeafIndex)
  1074  		}
  1075  
  1076  		extraData := leaf.ExtraData
  1077  		if len(extraData) == 0 {
  1078  			klog.Errorf("%s: Missing ExtraData for leaf %d", li.LogPrefix, leaf.LeafIndex)
  1079  		}
  1080  		jsonRsp.Entries = append(jsonRsp.Entries, ct.LeafEntry{
  1081  			LeafInput: leaf.LeafValue,
  1082  			ExtraData: extraData,
  1083  		})
  1084  	}
  1085  
  1086  	return jsonRsp, nil
  1087  }
  1088  
  1089  // checkAuditPath does a quick scan of the proof we got from the backend for consistency.
  1090  // All the hashes should be non zero length.
  1091  func checkAuditPath(path [][]byte) bool {
  1092  	for _, node := range path {
  1093  		if len(node) != sha256.Size {
  1094  			return false
  1095  		}
  1096  	}
  1097  	return true
  1098  }
  1099  
  1100  func (li *logInfo) toHTTPStatus(err error) int {
  1101  	if li.instanceOpts.ErrorMapper != nil {
  1102  		if status, ok := li.instanceOpts.ErrorMapper(err); ok {
  1103  			return status
  1104  		}
  1105  	}
  1106  
  1107  	rpcStatus, ok := status.FromError(err)
  1108  	if !ok {
  1109  		return http.StatusInternalServerError
  1110  	}
  1111  
  1112  	switch rpcStatus.Code() {
  1113  	case codes.OK:
  1114  		return http.StatusOK
  1115  	case codes.Canceled, codes.DeadlineExceeded:
  1116  		return http.StatusGatewayTimeout
  1117  	case codes.InvalidArgument, codes.OutOfRange, codes.AlreadyExists:
  1118  		return http.StatusBadRequest
  1119  	case codes.NotFound:
  1120  		return http.StatusNotFound
  1121  	case codes.PermissionDenied, codes.ResourceExhausted:
  1122  		return http.StatusForbidden
  1123  	case codes.Unauthenticated:
  1124  		return http.StatusUnauthorized
  1125  	case codes.FailedPrecondition:
  1126  		return http.StatusPreconditionFailed
  1127  	case codes.Aborted:
  1128  		return http.StatusConflict
  1129  	case codes.Unimplemented:
  1130  		return http.StatusNotImplemented
  1131  	case codes.Unavailable:
  1132  		return http.StatusServiceUnavailable
  1133  	default:
  1134  		return http.StatusInternalServerError
  1135  	}
  1136  }
  1137  
  1138  // recordStartPercent works out what percentage of the current log size an index corresponds to,
  1139  // and records this to the getEntriesStartPercentiles histogram.
  1140  func recordStartPercent(leafIndex int64, treeSize uint64, labelVals ...string) {
  1141  	if treeSize > 0 {
  1142  		percent := float64(leafIndex) / float64(treeSize) * 100.0
  1143  		getEntriesStartPercentiles.Observe(percent, labelVals...)
  1144  	}
  1145  }
  1146  

View as plain text