Skip to content

Commit

Permalink
Added support for sha384/sha512 hash algorithms in hashedrekords (#1959)
Browse files Browse the repository at this point in the history
* Added support for sha384/sha512 hash algorithms in hashedrekords

Includes changes provided by @bobcallaway

Signed-off-by: Riccardo Schirone <riccardo.schirone@trailofbits.com>

* Added e2e test for hashedrekord type

Make sure Rekor accepts SHA256 digest but not SHA1

Signed-off-by: Riccardo Schirone <riccardo.schirone@trailofbits.com>

* hashedrekord: add a SHA1 backstop test for CreateFromArtifactProperties

Signed-off-by: William Woodruff <william@trailofbits.com>

---------

Signed-off-by: Riccardo Schirone <riccardo.schirone@trailofbits.com>
Signed-off-by: William Woodruff <william@trailofbits.com>
Co-authored-by: William Woodruff <william@trailofbits.com>
  • Loading branch information
ret2libc and woodruffw authored Jan 23, 2024
1 parent 9a17431 commit 9865ca9
Show file tree
Hide file tree
Showing 8 changed files with 389 additions and 31 deletions.
12 changes: 9 additions & 3 deletions pkg/generated/models/hashedrekord_v001_schema.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 12 additions & 6 deletions pkg/generated/restapi/embedded_spec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

94 changes: 94 additions & 0 deletions pkg/types/hashedrekord/v0.0.1/e2e_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//
// Copyright 2024 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.

//go:build e2e

package hashedrekord

import (
"bytes"
"context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"os"
"testing"
"time"

"github.com/sigstore/rekor/pkg/client"
"github.com/sigstore/rekor/pkg/generated/client/entries"
"github.com/sigstore/rekor/pkg/types"
"github.com/sigstore/sigstore/pkg/cryptoutils"
"github.com/sigstore/sigstore/pkg/signature"
)

func rekorServer() string {
if s := os.Getenv("REKOR_SERVER"); s != "" {
return s
}
return "http://localhost:3000"
}

// TestSHA256HashedRekordEntry tests sending a valid HashedRekord proposed entry.
func TestSHA256HashedRekordEntry(t *testing.T) {
privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
t.Fatalf("error generating key: %v", err)
}
pubBytes, err := cryptoutils.MarshalPublicKeyToPEM(privKey.Public())
if err != nil {
t.Fatalf("error marshaling public key: %v", err)
}

data := []byte("data")
signer, err := signature.LoadSigner(privKey, crypto.SHA256)
if err != nil {
t.Fatalf("error loading verifier: %v", err)
}
signature, err := signer.SignMessage(bytes.NewReader(data))
if err != nil {
t.Fatalf("error signing message: %v", err)
}

ap := types.ArtifactProperties{
ArtifactBytes: []byte("data"),
ArtifactHash: "sha256:3a6eb0790f39ac87c94f3856b2dd2c5d110e6811602261a9a923d3bb23adc8b7",
PublicKeyBytes: [][]byte{pubBytes},
PKIFormat: "x509",
SignatureBytes: signature,
}

ei := NewEntry()

entry, err := ei.CreateFromArtifactProperties(context.Background(), ap)
if err != nil {
t.Fatalf("error creating entry: %v", err)
}

rc, err := client.GetRekorClient(rekorServer())
if err != nil {
t.Errorf("error getting client: %v", err)
}

params := &entries.CreateLogEntryParams{}
params.SetProposedEntry(entry)
params.SetContext(context.Background())
params.SetTimeout(5 * time.Second)

if _, err = rc.Entries.CreateLogEntry(params); err != nil {
t.Fatalf("expected no errors when submitting hashedrekord entry with sha256 to rekor %s", err)
}
}
30 changes: 27 additions & 3 deletions pkg/types/hashedrekord/v0.0.1/entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package hashedrekord
import (
"bytes"
"context"
"crypto"
"crypto/ed25519"
"crypto/sha256"
"encoding/hex"
Expand All @@ -38,6 +39,7 @@ import (
"github.com/sigstore/rekor/pkg/pki/x509"
"github.com/sigstore/rekor/pkg/types"
hashedrekord "github.com/sigstore/rekor/pkg/types/hashedrekord"
"github.com/sigstore/rekor/pkg/util"
"github.com/sigstore/sigstore/pkg/signature/options"
)

Expand Down Expand Up @@ -178,17 +180,38 @@ func (v *V001Entry) validate() (pki.Signature, pki.PublicKey, error) {
return nil, nil, types.ValidationError(errors.New("invalid value for hash"))
}

var alg crypto.Hash
switch swag.StringValue(hash.Algorithm) {
case models.HashedrekordV001SchemaDataHashAlgorithmSha384:
alg = crypto.SHA384
case models.HashedrekordV001SchemaDataHashAlgorithmSha512:
alg = crypto.SHA512
default:
alg = crypto.SHA256
}

decoded, err := hex.DecodeString(*hash.Value)
if err != nil {
return nil, nil, err
}
if err := sigObj.Verify(nil, keyObj, options.WithDigest(decoded)); err != nil {
if err := sigObj.Verify(nil, keyObj, options.WithDigest(decoded), options.WithCryptoSignerOpts(alg)); err != nil {
return nil, nil, types.ValidationError(fmt.Errorf("verifying signature: %w", err))
}

return sigObj, keyObj, nil
}

func getDataHashAlgorithm(hashAlgorithm crypto.Hash) string {
switch hashAlgorithm {
case crypto.SHA384:
return models.HashedrekordV001SchemaDataHashAlgorithmSha384
case crypto.SHA512:
return models.HashedrekordV001SchemaDataHashAlgorithmSha512
default:
return models.HashedrekordV001SchemaDataHashAlgorithmSha256
}
}

func (v V001Entry) CreateFromArtifactProperties(_ context.Context, props types.ArtifactProperties) (models.ProposedEntry, error) {
returnVal := models.Hashedrekord{}
re := V001Entry{}
Expand Down Expand Up @@ -230,10 +253,11 @@ func (v V001Entry) CreateFromArtifactProperties(_ context.Context, props types.A
return nil, errors.New("only one public key must be provided")
}

hashAlgorithm, hashValue := util.UnprefixSHA(props.ArtifactHash)
re.HashedRekordObj.Signature.PublicKey.Content = strfmt.Base64(publicKeyBytes[0])
re.HashedRekordObj.Data.Hash = &models.HashedrekordV001SchemaDataHash{
Algorithm: swag.String(models.HashedrekordV001SchemaDataHashAlgorithmSha256),
Value: swag.String(props.ArtifactHash),
Algorithm: swag.String(getDataHashAlgorithm(hashAlgorithm)),
Value: swag.String(hashValue),
}

if _, _, err := re.validate(); err != nil {
Expand Down
Loading

0 comments on commit 9865ca9

Please sign in to comment.