Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: sign-payload shouldn't recanonicalize payload #479

Merged
merged 1 commit into from
Apr 19, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 3 additions & 5 deletions cmd/tuf/sign_payload.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (

"github.com/flynn/go-docopt"
tuf "github.com/theupdateframework/go-tuf"
tufdata "github.com/theupdateframework/go-tuf/data"
)

func init() {
Expand All @@ -25,19 +24,18 @@ func cmdSignPayload(args *docopt.Args, repo *tuf.Repo) error {
if err != nil {
return err
}
signed := tufdata.Signed{Signed: payload, Signatures: make([]tufdata.Signature, 0)}

numKeys, err := repo.SignPayload(args.String["--role"], &signed)
signatures, err := repo.SignRaw(args.String["--role"], payload)
if err != nil {
return err
}
fmt.Fprintln(os.Stderr, "tuf: signed")

bytes, err := json.Marshal(signed.Signatures)
bytes, err := json.Marshal(signatures)
if err != nil {
return err
}
fmt.Fprint(os.Stdout, string(bytes))

fmt.Fprintln(os.Stderr, "tuf: signed with", numKeys, "key(s)")
return nil
}
41 changes: 38 additions & 3 deletions repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -782,11 +782,13 @@ func (r *Repo) setMeta(roleFilename string, meta interface{}) error {
return r.local.SetMeta(roleFilename, b)
}

// SignPayload signs the given payload using the key(s) associated with role.
// CanonicalizeAndSign canonicalizes the signed portion of signed, then signs it using the key(s) associated with role.
znewman01 marked this conversation as resolved.
Show resolved Hide resolved
//
// It appends the signature to signed.
//
// It returns the total number of keys used for signing, 0 (along with
// ErrNoKeys) if no keys were found, or -1 (along with an error) in error cases.
func (r *Repo) SignPayload(role string, payload *data.Signed) (int, error) {
func (r *Repo) CanonicalizeAndSign(role string, signed *data.Signed) (int, error) {
keys, err := r.signersForRole(role)
if err != nil {
return -1, err
Expand All @@ -795,13 +797,46 @@ func (r *Repo) SignPayload(role string, payload *data.Signed) (int, error) {
return 0, ErrNoKeys{role}
}
for _, k := range keys {
if err = sign.Sign(payload, k); err != nil {
if err = sign.Sign(signed, k); err != nil {
return -1, err
}
}
return len(keys), nil
}

// SignPayload canonicalizes the signed portion of payload, then signs it using the key(s) associated with role.
znewman01 marked this conversation as resolved.
Show resolved Hide resolved
//
// It returns the total number of keys used for signing, 0 (along with
// ErrNoKeys) if no keys were found, or -1 (along with an error) in error cases.
//
// DEPRECATED: please use CanonicalizeAndSign instead.
func (r *Repo) SignPayload(role string, payload *data.Signed) (int, error) {
return r.CanonicalizeAndSign(role, payload)
}

// SignRaw signs the given (pre-canonicalized) payload using the key(s) associated with role.
//
// It returns the new data.Signatures.
func (r *Repo) SignRaw(role string, payload []byte) ([]data.Signature, error) {
keys, err := r.signersForRole(role)
if err != nil {
return nil, err
}
if len(keys) == 0 {
return nil, ErrNoKeys{role}
}

allSigs := make([]data.Signature, 0, len(keys))
for _, k := range keys {
sigs, err := sign.MakeSignatures(payload, k)
if err != nil {
return nil, err
}
allSigs = append(allSigs, sigs...)
}
return allSigs, nil
}

func (r *Repo) Sign(roleFilename string) error {
signed, err := r.SignedMeta(roleFilename)
if err != nil {
Expand Down
12 changes: 6 additions & 6 deletions repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2601,7 +2601,8 @@ func (rs *RepoSuite) TestOfflineFlow(c *C) {
r, err := NewRepo(local)
c.Assert(err, IsNil)
c.Assert(r.Init(false), IsNil)
_, err = r.GenKey("root")
// Use ECDSA because it has a newline which is a difference between JSON and cJSON.
_, err = r.GenKeyWithSchemeAndExpires("root", data.DefaultExpires("root"), data.KeySchemeECDSA_SHA2_P256)
c.Assert(err, IsNil)

// Get the payload to sign
Expand All @@ -2621,15 +2622,14 @@ func (rs *RepoSuite) TestOfflineFlow(c *C) {
}

// Sign the payload
signed := data.Signed{Signed: payload}
_, err = r.SignPayload("targets", &signed)
_, err = r.SignRaw("targets", payload)
c.Assert(err, Equals, ErrNoKeys{"targets"})
numKeys, err := r.SignPayload("root", &signed)
signatures, err := r.SignRaw("root", payload)
c.Assert(err, IsNil)
c.Assert(numKeys, Equals, 1)
c.Assert(len(signatures), Equals, 1)

// Add the payload signatures back
for _, sig := range signed.Signatures {
for _, sig := range signatures {
// This method checks that the signature verifies!
err = r.AddOrUpdateSignature("root.json", sig)
c.Assert(err, IsNil)
Expand Down
61 changes: 40 additions & 21 deletions sign/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,65 @@ package sign

import (
"encoding/json"
"errors"

"github.com/secure-systems-lab/go-securesystemslib/cjson"
"github.com/theupdateframework/go-tuf/data"
"github.com/theupdateframework/go-tuf/pkg/keys"
)

func Sign(s *data.Signed, k keys.Signer) error {
const maxSignatures = 1024

// MakeSignatures creates data.Signatures for canonical using signer k.
//
// There will be one data.Signature for each of k's IDs, each wih the same
// signature data.
func MakeSignatures(canonical []byte, k keys.Signer) ([]data.Signature, error) {
sigData, err := k.SignMessage(canonical)
if err != nil {
return nil, err
}

ids := k.PublicData().IDs()
rdimitrov marked this conversation as resolved.
Show resolved Hide resolved
signatures := make([]data.Signature, 0, len(s.Signatures)+1)
for _, sig := range s.Signatures {
found := false
for _, id := range ids {
if sig.KeyID == id {
found = true
break
}
}
if !found {
signatures = append(signatures, sig)
}
signatures := make([]data.Signature, 0, len(ids))
for _, id := range ids {
signatures = append(signatures, data.Signature{
KeyID: id,
Signature: sigData,
})
}

return signatures, nil
}

// Sign signs the to-be-signed part of s using the signer k.
//
// The new signature(s) (one for each of k's key IDs) are appended to
// s.Signatures. Existing signatures for the Key IDs are replaced.
func Sign(s *data.Signed, k keys.Signer) error {
canonical, err := cjson.EncodeCanonical(s.Signed)
if err != nil {
return err
}

sig, err := k.SignMessage(canonical)
size := len(s.Signatures)
if size > maxSignatures-1 {
return errors.New("value too large")
}
signatures := make([]data.Signature, 0, size+1)
rdimitrov marked this conversation as resolved.
Show resolved Hide resolved
for _, oldSig := range s.Signatures {
if !k.PublicData().ContainsID(oldSig.KeyID) {
signatures = append(signatures, oldSig)
}
}

newSigs, err := MakeSignatures(canonical, k)
if err != nil {
return err
}
signatures = append(signatures, newSigs...)

s.Signatures = signatures
for _, id := range ids {
s.Signatures = append(s.Signatures, data.Signature{
KeyID: id,
Signature: sig,
})
}

return nil
}

Expand Down