Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,11 @@ func NewAPI(treeID int64) (*API, error) {
if !ok {
return nil, fmt.Errorf("no root found for inactive shard %d", r.TreeID)
}
cp, err := util.CreateAndSignCheckpoint(ctx, viper.GetString("rekor_server.hostname"), r.TreeID, uint64(r.TreeLength), root.RootHash, r.Signer)
treeLength, err := util.SafeInt64ToUint64(r.TreeLength)
if err != nil {
return nil, err
}
cp, err := util.CreateAndSignCheckpoint(ctx, viper.GetString("rekor_server.hostname"), r.TreeID, treeLength, root.RootHash, r.Signer)
if err != nil {
return nil, fmt.Errorf("error signing checkpoint for inactive shard %d: %w", r.TreeID, err)
}
Expand Down
14 changes: 12 additions & 2 deletions pkg/api/entries.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,13 @@ func logEntryFromLeaf(ctx context.Context, leaf *trillian.LogLeaf, signedLogRoot
sc = string(scBytes)
}

treeSize, err := util.SafeUint64ToInt64(root.TreeSize)
if err != nil {
return nil, err
}

inclusionProof := models.InclusionProof{
TreeSize: conv.Pointer(int64(root.TreeSize)),
TreeSize: conv.Pointer(treeSize),
RootHash: conv.Pointer(hex.EncodeToString(root.RootHash)),
LogIndex: conv.Pointer(proof.GetLeafIndex()),
Hashes: hashes,
Expand Down Expand Up @@ -445,8 +450,13 @@ func createLogEntry(params entries.CreateLogEntryParams) (models.LogEntry, middl
return nil, handleRekorAPIError(params, http.StatusInternalServerError, err, sthGenerateError)
}

treeSize, err := util.SafeUint64ToInt64(root.TreeSize)
if err != nil {
return nil, handleRekorAPIError(params, http.StatusInternalServerError, err, validationError)
}

inclusionProof := models.InclusionProof{
TreeSize: conv.Pointer(int64(root.TreeSize)),
TreeSize: conv.Pointer(treeSize),
RootHash: conv.Pointer(hex.EncodeToString(root.RootHash)),
LogIndex: conv.Pointer(queuedLeaf.LeafIndex),
Hashes: hashes,
Expand Down
10 changes: 8 additions & 2 deletions pkg/api/tlog.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ func GetLogInfoHandler(params tlog.GetLogInfoParams) middleware.Responder {
}

hashString := hex.EncodeToString(root.RootHash)
treeSize := int64(root.TreeSize)
treeSize, err := util.SafeUint64ToInt64(root.TreeSize)
if err != nil {
return handleRekorAPIError(params, http.StatusInternalServerError, err, validationError)
}

scBytes, err := util.CreateAndSignCheckpoint(ctx,
viper.GetString("rekor_server.hostname"), api.logRanges.GetActive().TreeID, root.TreeSize, root.RootHash, api.logRanges.GetActive().Signer)
Expand Down Expand Up @@ -164,7 +167,10 @@ func inactiveShardLogInfo(ctx context.Context, tid int64, cachedCheckpoints map[
}

hashString := hex.EncodeToString(root.RootHash)
treeSize := int64(root.TreeSize)
treeSize, err := util.SafeUint64ToInt64(root.TreeSize)
if err != nil {
return nil, err
}

m := models.InactiveShardLogInfo{
RootHash: &hashString,
Expand Down
7 changes: 6 additions & 1 deletion pkg/sharding/ranges.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/sigstore/rekor/pkg/log"
"github.com/sigstore/rekor/pkg/signer"
"github.com/sigstore/rekor/pkg/trillianclient"
"github.com/sigstore/rekor/pkg/util"
"github.com/sigstore/sigstore/pkg/cryptoutils"
"github.com/sigstore/sigstore/pkg/signature"
"github.com/sigstore/sigstore/pkg/signature/options"
Expand Down Expand Up @@ -116,7 +117,11 @@ func (l *LogRanges) CompleteInitialization(ctx context.Context, tcm *trilliancli
if err := root.UnmarshalBinary(resp.GetLatestResult.SignedLogRoot.LogRoot); err != nil {
return nil, err
}
l.inactive[i].TreeLength = int64(root.TreeSize)
treeSize, err := util.SafeUint64ToInt64(root.TreeSize)
if err != nil {
return nil, err
}
l.inactive[i].TreeLength = treeSize
sthMap[r.TreeID] = root
}
return sthMap, nil
Expand Down
31 changes: 28 additions & 3 deletions pkg/trillianclient/trillian_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"github.com/google/trillian"
"github.com/google/trillian/client"
"github.com/google/trillian/types"
"github.com/sigstore/rekor/pkg/util"
)

// TrillianClient provides a wrapper around the Trillian client
Expand Down Expand Up @@ -236,15 +237,31 @@ func (t *TrillianClient) GetLeafAndProofByIndex(ctx context.Context, index int64
}
}

treeSize, err := util.SafeUint64ToInt64(root.TreeSize)
if err != nil {
return &Response{
Status: codes.OutOfRange,
Err: status.Error(codes.OutOfRange, err.Error()),
}
}

resp, err := t.client.GetEntryAndProof(ctx,
&trillian.GetEntryAndProofRequest{
LogId: t.logID,
LeafIndex: index,
TreeSize: int64(root.TreeSize),
TreeSize: treeSize,
})

if resp != nil && resp.Proof != nil {
if err := proof.VerifyInclusion(rfc6962.DefaultHasher, uint64(index), root.TreeSize, resp.GetLeaf().MerkleLeafHash, resp.Proof.Hashes, root.RootHash); err != nil {
u_index, err := util.SafeInt64ToUint64(index)
if err != nil {
return &Response{
Status: codes.OutOfRange,
Err: status.Error(codes.OutOfRange, err.Error()),
}

}
if err := proof.VerifyInclusion(rfc6962.DefaultHasher, u_index, root.TreeSize, resp.GetLeaf().MerkleLeafHash, resp.Proof.Hashes, root.RootHash); err != nil {
return &Response{
Status: status.Code(err),
Err: err,
Expand Down Expand Up @@ -320,11 +337,19 @@ func (t *TrillianClient) getProofByHash(ctx context.Context, hashValue []byte) *
}
}

treeSize, err := util.SafeUint64ToInt64(root.TreeSize)
if err != nil {
return &Response{
Status: codes.OutOfRange,
Err: status.Error(codes.OutOfRange, err.Error()),
}
}

resp, err := t.client.GetInclusionProofByHash(ctx,
&trillian.GetInclusionProofByHashRequest{
LogId: t.logID,
LeafHash: hashValue,
TreeSize: int64(root.TreeSize),
TreeSize: treeSize,
})

if resp != nil {
Expand Down
36 changes: 36 additions & 0 deletions pkg/util/safe_convertors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2022 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package util

import (
"fmt"
"math"
)

// Converts uint64 to int64 after checking bounds.
func SafeUint64ToInt64(u uint64) (int64, error) {
if u > math.MaxInt64 {
return 0, fmt.Errorf("value %d too large to convert to int64", u)
}
return int64(u), nil
}

// Converts int64 to uint64 after checking bounds.
func SafeInt64ToUint64(i int64) (uint64, error) {
if i < 0 {
return 0, fmt.Errorf("value %d is negative and cannot be converted to uint64", i)
}
return uint64(i), nil
}
130 changes: 130 additions & 0 deletions pkg/util/safe_convertors_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// Copyright 2022 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package util

import (
"fmt"
"math"
"testing"
)

func TestSafeUint64ToInt64(t *testing.T) {
tests := []struct {
name string
input uint64
want int64
wantErr bool
}{
{
name: "small positive number",
input: 42,
want: 42,
wantErr: false,
},
{
name: "zero value",
input: 0,
want: 0,
wantErr: false,
},
{
name: "max int64 boundary",
input: uint64(math.MaxInt64),
want: math.MaxInt64,
wantErr: false,
},
{
name: "overflow beyond int64",
input: uint64(math.MaxInt64) + 1,
wantErr: true,
},
{
name: "very large overflow",
input: math.MaxUint64,
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := SafeUint64ToInt64(tt.input)
if (err != nil) != tt.wantErr {
t.Fatalf("SafeUint64ToInt64(%d) error = %v, wantErr %t", tt.input, err, tt.wantErr)
}
if !tt.wantErr && got != tt.want {
t.Fatalf("SafeUint64ToInt64(%d) = %d, want %d", tt.input, got, tt.want)
}
})
}
}

func TestSafeInt64ToUint64(t *testing.T) {
tests := []struct {
name string
input int64
want uint64
wantErr bool
}{
{
name: "zero",
input: 0,
want: 0,
wantErr: false,
},
{
name: "positive value",
input: 42,
want: 42,
wantErr: false,
},
{
name: "max int64",
input: math.MaxInt64,
want: uint64(math.MaxInt64),
wantErr: false,
},
{
name: "negative value",
input: -1,
wantErr: true,
},
{
name: "large negative",
input: math.MinInt64,
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := SafeInt64ToUint64(tt.input)

if (err != nil) != tt.wantErr {
t.Fatalf("SafeInt64ToUint64(%d) error = %v, wantErr %t", tt.input, err, tt.wantErr)
}

if !tt.wantErr && got != tt.want {
t.Fatalf("SafeInt64ToUint64(%d) = %d, want %d", tt.input, got, tt.want)
}

if tt.wantErr && err != nil {
expectedMsg := fmt.Sprintf("value %d is negative and cannot be converted to uint64", tt.input)
if err.Error() != expectedMsg {
t.Fatalf("error message = %q, want %q", err.Error(), expectedMsg)
}
}
})
}
}
38 changes: 30 additions & 8 deletions pkg/verify/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,31 @@ import (
// and a second new STH. Callers MUST verify signature on the STHs'.
func ProveConsistency(ctx context.Context, rClient *client.Rekor,
oldSTH *util.SignedCheckpoint, newSTH *util.SignedCheckpoint, treeID string) error {
oldTreeSize := int64(oldSTH.Size)

if oldSTH.Size == 0 {
return errors.New("consistency proofs can not be computed starting from an empty log")
}

oldTreeSize, err := util.SafeUint64ToInt64(oldSTH.Size)
if err != nil {
return err
}
newTreeSize, err := util.SafeUint64ToInt64(newSTH.Size)
if err != nil {
return err
}

switch {
case oldTreeSize == 0:
return errors.New("consistency proofs can not be computed starting from an empty log")
case oldTreeSize == int64(newSTH.Size):
case oldTreeSize == newTreeSize:
if !bytes.Equal(oldSTH.Hash, newSTH.Hash) {
return errors.New("old root hash does not match STH hash")
}
case oldTreeSize < int64(newSTH.Size):
case oldTreeSize < newTreeSize:
consistencyParams := tlog.NewGetLogProofParamsWithContext(ctx)
consistencyParams.FirstSize = &oldTreeSize // Root size at the old, or trusted state.
consistencyParams.LastSize = int64(newSTH.Size) // Root size at the new state to verify against.
consistencyParams.FirstSize = &oldTreeSize // Root size at the old, or trusted state.
consistencyParams.LastSize = newTreeSize // Root size at the new state to verify against.
consistencyParams.TreeID = &treeID
consistencyProof, err := rClient.Tlog.GetLogProof(consistencyParams)
if err != nil {
Expand All @@ -68,7 +81,7 @@ func ProveConsistency(ctx context.Context, rClient *client.Rekor,
oldSTH.Size, newSTH.Size, hashes, oldSTH.Hash, newSTH.Hash); err != nil {
return err
}
case oldTreeSize > int64(newSTH.Size):
case oldTreeSize > newTreeSize:
return errors.New("inclusion proof returned a tree size larger than the verified tree size")
}
return nil
Expand Down Expand Up @@ -161,8 +174,17 @@ func VerifyInclusion(ctx context.Context, e *models.LogEntryAnon) error {
}
leafHash := rfc6962.DefaultHasher.HashLeaf(entryBytes)

if err := proof.VerifyInclusion(rfc6962.DefaultHasher, uint64(*e.Verification.InclusionProof.LogIndex),
uint64(*e.Verification.InclusionProof.TreeSize), leafHash, hashes, rootHash); err != nil {
logIndex, err := util.SafeInt64ToUint64(*e.Verification.InclusionProof.LogIndex)
if err != nil {
return err
}
treeSize, err := util.SafeInt64ToUint64(*e.Verification.InclusionProof.TreeSize)
if err != nil {
return err
}

if err := proof.VerifyInclusion(rfc6962.DefaultHasher, logIndex,
treeSize, leafHash, hashes, rootHash); err != nil {
return err
}

Expand Down
Loading