...

Source file src/github.com/sigstore/rekor/pkg/util/checkpoint.go

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

     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 util
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	"encoding/base64"
    22  	"errors"
    23  	"fmt"
    24  	"strconv"
    25  	"strings"
    26  
    27  	"github.com/sigstore/sigstore/pkg/signature"
    28  	"github.com/sigstore/sigstore/pkg/signature/options"
    29  )
    30  
    31  // heavily borrowed from https://github.com/transparency-dev/formats/blob/main/log/checkpoint.go
    32  
    33  type Checkpoint struct {
    34  	// Origin is the unique identifier/version string
    35  	Origin string
    36  	// Size is the number of entries in the log at this checkpoint.
    37  	Size uint64
    38  	// Hash is the hash which commits to the contents of the entire log.
    39  	Hash []byte
    40  	// OtherContent is any additional data to be included in the signed payload; each element is assumed to be one line
    41  	OtherContent []string
    42  }
    43  
    44  // String returns the String representation of the Checkpoint
    45  func (c Checkpoint) String() string {
    46  	var b strings.Builder
    47  	fmt.Fprintf(&b, "%s\n%d\n%s\n", c.Origin, c.Size, base64.StdEncoding.EncodeToString(c.Hash))
    48  	for _, line := range c.OtherContent {
    49  		fmt.Fprintf(&b, "%s\n", line)
    50  	}
    51  	return b.String()
    52  }
    53  
    54  // MarshalText returns the common format representation of this Checkpoint.
    55  func (c Checkpoint) MarshalCheckpoint() ([]byte, error) {
    56  	return []byte(c.String()), nil
    57  }
    58  
    59  // UnmarshalText parses the common formatted checkpoint data and stores the result
    60  // in the Checkpoint.
    61  //
    62  // The supplied data is expected to begin with the following 3 lines of text,
    63  // each followed by a newline:
    64  // <ecosystem/version string>
    65  // <decimal representation of log size>
    66  // <base64 representation of root hash>
    67  // <optional non-empty line of other content>...
    68  // <optional non-empty line of other content>...
    69  //
    70  // This will discard any content found after the checkpoint (including signatures)
    71  func (c *Checkpoint) UnmarshalCheckpoint(data []byte) error {
    72  	l := bytes.Split(data, []byte("\n"))
    73  	if len(l) < 4 {
    74  		return errors.New("invalid checkpoint - too few newlines")
    75  	}
    76  	origin := string(l[0])
    77  	if len(origin) == 0 {
    78  		return errors.New("invalid checkpoint - empty ecosystem")
    79  	}
    80  	size, err := strconv.ParseUint(string(l[1]), 10, 64)
    81  	if err != nil {
    82  		return fmt.Errorf("invalid checkpoint - size invalid: %w", err)
    83  	}
    84  	h, err := base64.StdEncoding.DecodeString(string(l[2]))
    85  	if err != nil {
    86  		return fmt.Errorf("invalid checkpoint - invalid hash: %w", err)
    87  	}
    88  	*c = Checkpoint{
    89  		Origin: origin,
    90  		Size:   size,
    91  		Hash:   h,
    92  	}
    93  	if len(l) >= 3 {
    94  		for _, line := range l[3:] {
    95  			if len(line) == 0 {
    96  				break
    97  			}
    98  			c.OtherContent = append(c.OtherContent, string(line))
    99  		}
   100  	}
   101  	return nil
   102  }
   103  
   104  type SignedCheckpoint struct {
   105  	Checkpoint
   106  	SignedNote
   107  }
   108  
   109  func CreateSignedCheckpoint(c Checkpoint) (*SignedCheckpoint, error) {
   110  	text, err := c.MarshalCheckpoint()
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  	return &SignedCheckpoint{
   115  		Checkpoint: c,
   116  		SignedNote: SignedNote{Note: string(text)},
   117  	}, nil
   118  }
   119  
   120  func SignedCheckpointValidator(strToValidate string) bool {
   121  	s := SignedNote{}
   122  	if err := s.UnmarshalText([]byte(strToValidate)); err != nil {
   123  		return false
   124  	}
   125  	c := &Checkpoint{}
   126  	return c.UnmarshalCheckpoint([]byte(s.Note)) == nil
   127  }
   128  
   129  func CheckpointValidator(strToValidate string) bool {
   130  	c := &Checkpoint{}
   131  	return c.UnmarshalCheckpoint([]byte(strToValidate)) == nil
   132  }
   133  
   134  func (r *SignedCheckpoint) UnmarshalText(data []byte) error {
   135  	s := SignedNote{}
   136  	if err := s.UnmarshalText([]byte(data)); err != nil {
   137  		return fmt.Errorf("unmarshalling signed note: %w", err)
   138  	}
   139  	c := Checkpoint{}
   140  	if err := c.UnmarshalCheckpoint([]byte(s.Note)); err != nil {
   141  		return fmt.Errorf("unmarshalling checkpoint: %w", err)
   142  	}
   143  	*r = SignedCheckpoint{Checkpoint: c, SignedNote: s}
   144  	return nil
   145  }
   146  
   147  // CreateAndSignCheckpoint creates a signed checkpoint as a commitment to the current root hash
   148  func CreateAndSignCheckpoint(ctx context.Context, hostname string, treeID int64, treeSize uint64, rootHash []byte, signer signature.Signer) ([]byte, error) {
   149  	sth, err := CreateSignedCheckpoint(Checkpoint{
   150  		Origin: fmt.Sprintf("%s - %d", hostname, treeID),
   151  		Size:   treeSize,
   152  		Hash:   rootHash,
   153  	})
   154  	if err != nil {
   155  		return nil, fmt.Errorf("error creating checkpoint: %w", err)
   156  	}
   157  	if _, err := sth.Sign(hostname, signer, options.WithContext(ctx)); err != nil {
   158  		return nil, fmt.Errorf("error signing checkpoint: %w", err)
   159  	}
   160  	scBytes, err := sth.SignedNote.MarshalText()
   161  	if err != nil {
   162  		return nil, fmt.Errorf("error marshalling checkpoint: %w", err)
   163  	}
   164  	return scBytes, nil
   165  }
   166  

View as plain text