...

Source file src/github.com/sigstore/rekor/pkg/sharding/ranges.go

Documentation: github.com/sigstore/rekor/pkg/sharding

     1  //
     2  // Copyright 2021 The Sigstore Authors.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package sharding
    17  
    18  import (
    19  	"context"
    20  	"encoding/base64"
    21  	"encoding/json"
    22  	"errors"
    23  	"fmt"
    24  	"os"
    25  	"strconv"
    26  	"strings"
    27  
    28  	"github.com/google/trillian"
    29  	"github.com/google/trillian/types"
    30  	"github.com/sigstore/rekor/pkg/log"
    31  	"sigs.k8s.io/yaml"
    32  )
    33  
    34  type LogRanges struct {
    35  	inactive Ranges
    36  	active   int64
    37  }
    38  
    39  type Ranges []LogRange
    40  
    41  type LogRange struct {
    42  	TreeID           int64  `json:"treeID" yaml:"treeID"`
    43  	TreeLength       int64  `json:"treeLength" yaml:"treeLength"`
    44  	EncodedPublicKey string `json:"encodedPublicKey" yaml:"encodedPublicKey"`
    45  	decodedPublicKey string
    46  }
    47  
    48  func NewLogRanges(ctx context.Context, logClient trillian.TrillianLogClient, path string, treeID uint) (LogRanges, error) {
    49  	if path == "" {
    50  		log.Logger.Info("No config file specified, skipping init of logRange map")
    51  		return LogRanges{}, nil
    52  	}
    53  	if treeID == 0 {
    54  		return LogRanges{}, errors.New("non-zero tlog_id required when passing in shard config filepath; please set the active tree ID via the `--trillian_log_server.tlog_id` flag")
    55  	}
    56  	// otherwise, try to read contents of the sharding config
    57  	ranges, err := logRangesFromPath(path)
    58  	if err != nil {
    59  		return LogRanges{}, fmt.Errorf("log ranges from path: %w", err)
    60  	}
    61  	for i, r := range ranges {
    62  		r, err := updateRange(ctx, logClient, r)
    63  		if err != nil {
    64  			return LogRanges{}, fmt.Errorf("updating range for tree id %d: %w", r.TreeID, err)
    65  		}
    66  		ranges[i] = r
    67  	}
    68  	log.Logger.Info("Ranges: %v", ranges)
    69  	return LogRanges{
    70  		inactive: ranges,
    71  		active:   int64(treeID),
    72  	}, nil
    73  }
    74  
    75  func logRangesFromPath(path string) (Ranges, error) {
    76  	var ranges Ranges
    77  	contents, err := os.ReadFile(path)
    78  	if err != nil {
    79  		return Ranges{}, err
    80  	}
    81  	if string(contents) == "" {
    82  		log.Logger.Info("Sharding config file contents empty, skipping init of logRange map")
    83  		return Ranges{}, nil
    84  	}
    85  	if err := yaml.Unmarshal(contents, &ranges); err != nil {
    86  		// Try to use JSON
    87  		if jerr := json.Unmarshal(contents, &ranges); jerr == nil {
    88  			return ranges, nil
    89  		}
    90  		return Ranges{}, err
    91  	}
    92  	return ranges, nil
    93  }
    94  
    95  // updateRange fills in any missing information about the range
    96  func updateRange(ctx context.Context, logClient trillian.TrillianLogClient, r LogRange) (LogRange, error) {
    97  	// If a tree length wasn't passed in, get it ourselves
    98  	if r.TreeLength == 0 {
    99  		resp, err := logClient.GetLatestSignedLogRoot(ctx, &trillian.GetLatestSignedLogRootRequest{LogId: r.TreeID})
   100  		if err != nil {
   101  			return LogRange{}, fmt.Errorf("getting signed log root for tree %d: %w", r.TreeID, err)
   102  		}
   103  		var root types.LogRootV1
   104  		if err := root.UnmarshalBinary(resp.SignedLogRoot.LogRoot); err != nil {
   105  			return LogRange{}, err
   106  		}
   107  		r.TreeLength = int64(root.TreeSize)
   108  	}
   109  	// If a public key was provided, decode it
   110  	if r.EncodedPublicKey != "" {
   111  		decoded, err := base64.StdEncoding.DecodeString(r.EncodedPublicKey)
   112  		if err != nil {
   113  			return LogRange{}, err
   114  		}
   115  		r.decodedPublicKey = string(decoded)
   116  	}
   117  	return r, nil
   118  }
   119  
   120  func (l *LogRanges) ResolveVirtualIndex(index int) (int64, int64) {
   121  	indexLeft := index
   122  	for _, l := range l.inactive {
   123  		if indexLeft < int(l.TreeLength) {
   124  			return l.TreeID, int64(indexLeft)
   125  		}
   126  		indexLeft -= int(l.TreeLength)
   127  	}
   128  
   129  	// If index not found in inactive trees, return the active tree
   130  	return l.active, int64(indexLeft)
   131  }
   132  
   133  func (l *LogRanges) ActiveTreeID() int64 {
   134  	return l.active
   135  }
   136  
   137  func (l *LogRanges) NoInactive() bool {
   138  	return l.inactive == nil
   139  }
   140  
   141  // AllShards returns all shards, starting with the active shard and then the inactive shards
   142  func (l *LogRanges) AllShards() []int64 {
   143  	shards := []int64{l.ActiveTreeID()}
   144  	for _, in := range l.GetInactive() {
   145  		shards = append(shards, in.TreeID)
   146  	}
   147  	return shards
   148  }
   149  
   150  // TotalInactiveLength returns the total length across all inactive shards;
   151  // we don't know the length of the active shard.
   152  func (l *LogRanges) TotalInactiveLength() int64 {
   153  	var total int64
   154  	for _, r := range l.inactive {
   155  		total += r.TreeLength
   156  	}
   157  	return total
   158  }
   159  
   160  func (l *LogRanges) SetInactive(r []LogRange) {
   161  	l.inactive = r
   162  }
   163  
   164  func (l *LogRanges) GetInactive() []LogRange {
   165  	return l.inactive
   166  }
   167  
   168  func (l *LogRanges) AppendInactive(r LogRange) {
   169  	l.inactive = append(l.inactive, r)
   170  }
   171  
   172  func (l *LogRanges) SetActive(i int64) {
   173  	l.active = i
   174  }
   175  
   176  func (l *LogRanges) GetActive() int64 {
   177  	return l.active
   178  }
   179  
   180  func (l *LogRanges) String() string {
   181  	ranges := []string{}
   182  	for _, r := range l.inactive {
   183  		ranges = append(ranges, fmt.Sprintf("%d=%d", r.TreeID, r.TreeLength))
   184  	}
   185  	ranges = append(ranges, fmt.Sprintf("active=%d", l.active))
   186  	return strings.Join(ranges, ",")
   187  }
   188  
   189  // PublicKey returns the associated public key for the given Tree ID
   190  // and returns the active public key by default
   191  func (l *LogRanges) PublicKey(activePublicKey, treeID string) (string, error) {
   192  	// if no tree ID is specified, assume the active tree
   193  	if treeID == "" {
   194  		return activePublicKey, nil
   195  	}
   196  	tid, err := strconv.Atoi(treeID)
   197  	if err != nil {
   198  		return "", err
   199  	}
   200  
   201  	for _, i := range l.inactive {
   202  		if int(i.TreeID) == tid {
   203  			if i.decodedPublicKey != "" {
   204  				return i.decodedPublicKey, nil
   205  			}
   206  			// assume the active public key if one wasn't provided
   207  			return activePublicKey, nil
   208  		}
   209  	}
   210  	if tid == int(l.active) {
   211  		return activePublicKey, nil
   212  	}
   213  	return "", fmt.Errorf("%d is not a valid tree ID and doesn't have an associated public key", tid)
   214  }
   215  

View as plain text