Skip to content

Commit 88d792d

Browse files
authored
Update signature.go
1 parent d5acf4b commit 88d792d

1 file changed

Lines changed: 24 additions & 23 deletions

File tree

internal/receipt/signature.go

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ import (
44
"crypto/ed25519"
55
"errors"
66
"fmt"
7+
"strings"
78

89
"ix-agent-notary/internal/canon"
910
"ix-agent-notary/internal/crypto"
1011
)
1112

1213
type SignatureValidationOptions struct {
1314
Strict bool
14-
PublicKeyPath string // optional override for public key lookup (base64url file)
15+
PublicKeyPath string // optional exact public key file (base64url)
16+
PublicKeyDir string // optional directory containing <key_id>.pub
1517
}
1618

1719
type SignatureCheck struct {
@@ -35,6 +37,10 @@ func ValidateSignature(r Receipt, opts SignatureValidationOptions) (*SignatureCh
3537
keyID, _ := sigObj["key_id"].(string)
3638
val, _ := sigObj["value"].(string)
3739

40+
alg = strings.ToLower(strings.TrimSpace(alg))
41+
keyID = strings.TrimSpace(keyID)
42+
val = strings.TrimSpace(val)
43+
3844
if isPlaceholder(val) {
3945
if opts.Strict {
4046
return nil, fmt.Errorf("signature is missing/placeholder")
@@ -49,25 +55,16 @@ func ValidateSignature(r Receipt, opts SignatureValidationOptions) (*SignatureCh
4955
return nil, fmt.Errorf("missing integrity.signature.key_id")
5056
}
5157

52-
var pub ed25519.PublicKey
53-
if opts.PublicKeyPath != "" {
54-
p, err := crypto.LoadEd25519PublicKeyFile(opts.PublicKeyPath)
55-
if err != nil {
56-
if opts.Strict {
57-
return nil, err
58-
}
59-
return &SignatureCheck{Skipped: true, Alg: alg, KeyID: keyID}, nil
60-
}
61-
pub = p
62-
} else {
63-
p, _, err := crypto.ResolvePublicKeyByID(keyID)
64-
if err != nil {
65-
if opts.Strict {
66-
return nil, err
67-
}
68-
return &SignatureCheck{Skipped: true, Alg: alg, KeyID: keyID}, nil
58+
pub, _, err := crypto.ResolveEd25519PublicKey(crypto.ResolvePublicKeyOptions{
59+
KeyID: keyID,
60+
PublicKeyPath: opts.PublicKeyPath,
61+
SearchDirs: receiptPublicKeySearchDirs(opts.PublicKeyDir),
62+
})
63+
if err != nil {
64+
if opts.Strict {
65+
return nil, err
6966
}
70-
pub = p
67+
return &SignatureCheck{Skipped: true, Alg: alg, KeyID: keyID}, nil
7168
}
7269

7370
sigBytes, err := crypto.DecodeBase64URLNoPad(val)
@@ -91,13 +88,11 @@ func ValidateSignature(r Receipt, opts SignatureValidationOptions) (*SignatureCh
9188
}
9289

9390
func canonicalBytesForSignature(r Receipt) ([]byte, error) {
94-
// Deep-clone via JSON round-trip to avoid mutating the original receipt.
9591
cloned, err := cloneReceipt(r)
9692
if err != nil {
9793
return nil, err
9894
}
9995

100-
// Remove signature.value before canonicalization (normative rule in spec).
10196
if integrity, ok := cloned["integrity"].(map[string]any); ok {
10297
if sigObj, ok := integrity["signature"].(map[string]any); ok {
10398
delete(sigObj, "value")
@@ -112,11 +107,17 @@ func canonicalBytesForSignature(r Receipt) ([]byte, error) {
112107
}
113108

114109
func cloneReceipt(r Receipt) (map[string]any, error) {
115-
// Use the fact that Receipt is map[string]any; marshal/unmarshal gives a safe deep clone.
116-
// This is acceptable for verification tooling (not a hot path).
117110
b, err := marshalJSON(r)
118111
if err != nil {
119112
return nil, err
120113
}
121114
return unmarshalJSONObject(b)
122115
}
116+
117+
func receiptPublicKeySearchDirs(dir string) []string {
118+
dir = strings.TrimSpace(dir)
119+
if dir == "" {
120+
return nil
121+
}
122+
return []string{dir}
123+
}

0 commit comments

Comments
 (0)