Skip to content

Commit 725537e

Browse files
jkuhaydentherappercmurphy
authored
Create NoteVerifier, verification library functions (v2) (#119)
* Create NoteVerifier, verification library functions * verify: add tests Signed-off-by: Hayden B <8418760+haydentherapper@users.noreply.github.com> Signed-off-by: Jussi Kukkonen <jkukkonen@google.com> Co-authored-by: Hayden B <8418760+haydentherapper@users.noreply.github.com> Co-authored-by: Colleen Murphy <colleenmurphy@google.com>
1 parent 1eda0c4 commit 725537e

File tree

3 files changed

+365
-14
lines changed

3 files changed

+365
-14
lines changed

pkg/note/note.go

+73-14
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ package note
2121
import (
2222
"bytes"
2323
"context"
24+
"crypto"
2425
"crypto/ecdsa"
2526
"crypto/ed25519"
2627
"crypto/rsa"
@@ -67,6 +68,24 @@ func (n *noteSigner) Sign(msg []byte) ([]byte, error) {
6768
return n.sign(msg)
6869
}
6970

71+
type noteVerifier struct {
72+
name string
73+
hash uint32
74+
verify func(msg, sig []byte) bool
75+
}
76+
77+
func (n *noteVerifier) Name() string {
78+
return n.name
79+
}
80+
81+
func (n *noteVerifier) KeyHash() uint32 {
82+
return n.hash
83+
}
84+
85+
func (n *noteVerifier) Verify(msg, sig []byte) bool {
86+
return n.verify(msg, sig)
87+
}
88+
7089
// isValidName reports whether the name conforms to the spec for the origin string of the note text
7190
// as defined in https://github.com/C2SP/C2SP/blob/main/tlog-checkpoint.md#note-text.
7291
func isValidName(name string) bool {
@@ -112,32 +131,45 @@ func rsaKeyHash(name string, key *rsa.PublicKey) (uint32, error) {
112131
return genConformantKeyHash(name, rsaAlg, marshaled), nil
113132
}
114133

115-
// NewNoteSigner converts a sigstore/sigstore/pkg/signature.Signer into a note.Signer.
116-
func NewNoteSigner(ctx context.Context, origin string, signer signature.Signer) (note.Signer, error) {
117-
if !isValidName(origin) {
118-
return &noteSigner{}, fmt.Errorf("invalid name %s", origin)
119-
}
120-
121-
pubKey, err := signer.PublicKey()
122-
if err != nil {
123-
return &noteSigner{}, fmt.Errorf("getting public key: %w", err)
124-
}
134+
// keyHash generates a 4-byte identifier for a public key/origin
135+
func keyHash(origin string, key crypto.PublicKey) (uint32, error) {
125136
var keyID uint32
126-
switch pk := pubKey.(type) {
137+
var err error
138+
139+
switch pk := key.(type) {
127140
case *ecdsa.PublicKey:
128141
keyID, err = ecdsaKeyHash(pk)
129142
if err != nil {
130-
return &noteSigner{}, fmt.Errorf("getting ECDSA key hash: %w", err)
143+
return 0, fmt.Errorf("getting ECDSA key hash: %w", err)
131144
}
132145
case ed25519.PublicKey:
133146
keyID = ed25519KeyHash(origin, pk)
134147
case *rsa.PublicKey:
135148
keyID, err = rsaKeyHash(origin, pk)
136149
if err != nil {
137-
return &noteSigner{}, fmt.Errorf("getting RSA key hash: %w", err)
150+
return 0, fmt.Errorf("getting RSA key hash: %w", err)
138151
}
139152
default:
140-
return &noteSigner{}, fmt.Errorf("unsupported key type: %T", pubKey)
153+
return 0, fmt.Errorf("unsupported key type: %T", key)
154+
}
155+
156+
return keyID, nil
157+
}
158+
159+
// NewNoteSigner converts a sigstore/sigstore/pkg/signature.Signer into a note.Signer.
160+
func NewNoteSigner(ctx context.Context, origin string, signer signature.Signer) (note.Signer, error) {
161+
if !isValidName(origin) {
162+
return nil, fmt.Errorf("invalid name %s", origin)
163+
}
164+
165+
pubKey, err := signer.PublicKey()
166+
if err != nil {
167+
return nil, fmt.Errorf("getting public key: %w", err)
168+
}
169+
170+
keyID, err := keyHash(origin, pubKey)
171+
if err != nil {
172+
return nil, err
141173
}
142174

143175
sign := func(msg []byte) ([]byte, error) {
@@ -150,3 +182,30 @@ func NewNoteSigner(ctx context.Context, origin string, signer signature.Signer)
150182
sign: sign,
151183
}, nil
152184
}
185+
186+
func NewNoteVerifier(origin string, verifier signature.Verifier) (note.Verifier, error) {
187+
if !isValidName(origin) {
188+
return nil, fmt.Errorf("invalid name %s", origin)
189+
}
190+
191+
pubKey, err := verifier.PublicKey()
192+
if err != nil {
193+
return nil, fmt.Errorf("getting public key: %w", err)
194+
}
195+
196+
keyID, err := keyHash(origin, pubKey)
197+
if err != nil {
198+
return nil, err
199+
}
200+
201+
return &noteVerifier{
202+
name: origin,
203+
hash: keyID,
204+
verify: func(msg, sig []byte) bool {
205+
if err := verifier.VerifySignature(bytes.NewReader(sig), bytes.NewReader(msg)); err != nil {
206+
return false
207+
}
208+
return true
209+
},
210+
}, nil
211+
}

pkg/verify/verify.go

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//
2+
// Copyright 2025 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 verify
17+
18+
import (
19+
"fmt"
20+
21+
pbs "github.com/sigstore/protobuf-specs/gen/pb-go/rekor/v1"
22+
"github.com/sigstore/rekor-tiles/pkg/tessera"
23+
f_log "github.com/transparency-dev/formats/log"
24+
"github.com/transparency-dev/merkle/proof"
25+
"github.com/transparency-dev/merkle/rfc6962"
26+
sumdb_note "golang.org/x/mod/sumdb/note"
27+
)
28+
29+
// VerifyInclusionProof verifies an entry's inclusion proof
30+
func VerifyInclusionProof(entry *pbs.TransparencyLogEntry, cp *f_log.Checkpoint) error { //nolint: revive
31+
leafHash := rfc6962.DefaultHasher.HashLeaf(entry.CanonicalizedBody)
32+
index, err := tessera.NewSafeInt64(entry.LogIndex)
33+
if err != nil {
34+
return fmt.Errorf("invalid index: %w", err)
35+
}
36+
if err := proof.VerifyInclusion(rfc6962.DefaultHasher, index.U(), cp.Size, leafHash, entry.InclusionProof.Hashes, cp.Hash); err != nil {
37+
return fmt.Errorf("verifying inclusion: %w", err)
38+
}
39+
return nil
40+
}
41+
42+
// VerifyCheckpoint verifies the signature on the entry's inclusion proof checkpoint
43+
func VerifyCheckpoint(entry *pbs.TransparencyLogEntry, verifier sumdb_note.Verifier) (*f_log.Checkpoint, error) { //nolint: revive
44+
cp, _, _, err := f_log.ParseCheckpoint([]byte(entry.InclusionProof.GetCheckpoint().GetEnvelope()), verifier.Name(), verifier)
45+
if err != nil {
46+
return nil, fmt.Errorf("unverified checkpoint signature: %v", err)
47+
}
48+
return cp, nil
49+
}
50+
51+
// VerifyLogEntry verifies the log entry. This includes verifying the signature on the entry's
52+
// inclusion proof checkpoint and verifying the entry inclusion proof
53+
func VerifyLogEntry(entry *pbs.TransparencyLogEntry, verifier sumdb_note.Verifier) error { //nolint: revive
54+
cp, err := VerifyCheckpoint(entry, verifier)
55+
if err != nil {
56+
return err
57+
}
58+
return VerifyInclusionProof(entry, cp)
59+
}

0 commit comments

Comments
 (0)