Skip to content

Commit 6b93a5a

Browse files
authored
fix: sign-payload shouldn't recanonicalize payload (#479)
Fixes #475. - Add new `Repo.SignRaw` which doesn't canonicalize before signing (and just returns Signatures) - Rename `SignPayload` to `CanonicalizeAndSign` (old name is deprecated; it probably doesn't actually get used so we can rip out next major release.) - Add `sign.MakeSignatures` which does not canonicalize; refactor `sign.Sign` to use it. - Modify offline flow to test this property. - Use `SignRaw` in `tuf sign-payload`. Signed-off-by: Zachary Newman <[email protected]>
1 parent c7d649b commit 6b93a5a

File tree

4 files changed

+87
-35
lines changed

4 files changed

+87
-35
lines changed

cmd/tuf/sign_payload.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77

88
"github.com/flynn/go-docopt"
99
tuf "github.com/theupdateframework/go-tuf"
10-
tufdata "github.com/theupdateframework/go-tuf/data"
1110
)
1211

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

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

35-
bytes, err := json.Marshal(signed.Signatures)
34+
bytes, err := json.Marshal(signatures)
3635
if err != nil {
3736
return err
3837
}
3938
fmt.Fprint(os.Stdout, string(bytes))
4039

41-
fmt.Fprintln(os.Stderr, "tuf: signed with", numKeys, "key(s)")
4240
return nil
4341
}

repo.go

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -782,11 +782,13 @@ func (r *Repo) setMeta(roleFilename string, meta interface{}) error {
782782
return r.local.SetMeta(roleFilename, b)
783783
}
784784

785-
// SignPayload signs the given payload using the key(s) associated with role.
785+
// CanonicalizeAndSign canonicalizes the signed portion of signed, then signs it using the key(s) associated with role.
786+
//
787+
// It appends the signature to signed.
786788
//
787789
// It returns the total number of keys used for signing, 0 (along with
788790
// ErrNoKeys) if no keys were found, or -1 (along with an error) in error cases.
789-
func (r *Repo) SignPayload(role string, payload *data.Signed) (int, error) {
791+
func (r *Repo) CanonicalizeAndSign(role string, signed *data.Signed) (int, error) {
790792
keys, err := r.signersForRole(role)
791793
if err != nil {
792794
return -1, err
@@ -795,13 +797,46 @@ func (r *Repo) SignPayload(role string, payload *data.Signed) (int, error) {
795797
return 0, ErrNoKeys{role}
796798
}
797799
for _, k := range keys {
798-
if err = sign.Sign(payload, k); err != nil {
800+
if err = sign.Sign(signed, k); err != nil {
799801
return -1, err
800802
}
801803
}
802804
return len(keys), nil
803805
}
804806

807+
// SignPayload canonicalizes the signed portion of payload, then signs it using the key(s) associated with role.
808+
//
809+
// It returns the total number of keys used for signing, 0 (along with
810+
// ErrNoKeys) if no keys were found, or -1 (along with an error) in error cases.
811+
//
812+
// DEPRECATED: please use CanonicalizeAndSign instead.
813+
func (r *Repo) SignPayload(role string, payload *data.Signed) (int, error) {
814+
return r.CanonicalizeAndSign(role, payload)
815+
}
816+
817+
// SignRaw signs the given (pre-canonicalized) payload using the key(s) associated with role.
818+
//
819+
// It returns the new data.Signatures.
820+
func (r *Repo) SignRaw(role string, payload []byte) ([]data.Signature, error) {
821+
keys, err := r.signersForRole(role)
822+
if err != nil {
823+
return nil, err
824+
}
825+
if len(keys) == 0 {
826+
return nil, ErrNoKeys{role}
827+
}
828+
829+
allSigs := make([]data.Signature, 0, len(keys))
830+
for _, k := range keys {
831+
sigs, err := sign.MakeSignatures(payload, k)
832+
if err != nil {
833+
return nil, err
834+
}
835+
allSigs = append(allSigs, sigs...)
836+
}
837+
return allSigs, nil
838+
}
839+
805840
func (r *Repo) Sign(roleFilename string) error {
806841
signed, err := r.SignedMeta(roleFilename)
807842
if err != nil {

repo_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2601,7 +2601,8 @@ func (rs *RepoSuite) TestOfflineFlow(c *C) {
26012601
r, err := NewRepo(local)
26022602
c.Assert(err, IsNil)
26032603
c.Assert(r.Init(false), IsNil)
2604-
_, err = r.GenKey("root")
2604+
// Use ECDSA because it has a newline which is a difference between JSON and cJSON.
2605+
_, err = r.GenKeyWithSchemeAndExpires("root", data.DefaultExpires("root"), data.KeySchemeECDSA_SHA2_P256)
26052606
c.Assert(err, IsNil)
26062607

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

26232624
// Sign the payload
2624-
signed := data.Signed{Signed: payload}
2625-
_, err = r.SignPayload("targets", &signed)
2625+
_, err = r.SignRaw("targets", payload)
26262626
c.Assert(err, Equals, ErrNoKeys{"targets"})
2627-
numKeys, err := r.SignPayload("root", &signed)
2627+
signatures, err := r.SignRaw("root", payload)
26282628
c.Assert(err, IsNil)
2629-
c.Assert(numKeys, Equals, 1)
2629+
c.Assert(len(signatures), Equals, 1)
26302630

26312631
// Add the payload signatures back
2632-
for _, sig := range signed.Signatures {
2632+
for _, sig := range signatures {
26332633
// This method checks that the signature verifies!
26342634
err = r.AddOrUpdateSignature("root.json", sig)
26352635
c.Assert(err, IsNil)

sign/sign.go

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,46 +2,65 @@ package sign
22

33
import (
44
"encoding/json"
5+
"errors"
56

67
"github.com/secure-systems-lab/go-securesystemslib/cjson"
78
"github.com/theupdateframework/go-tuf/data"
89
"github.com/theupdateframework/go-tuf/pkg/keys"
910
)
1011

11-
func Sign(s *data.Signed, k keys.Signer) error {
12+
const maxSignatures = 1024
13+
14+
// MakeSignatures creates data.Signatures for canonical using signer k.
15+
//
16+
// There will be one data.Signature for each of k's IDs, each wih the same
17+
// signature data.
18+
func MakeSignatures(canonical []byte, k keys.Signer) ([]data.Signature, error) {
19+
sigData, err := k.SignMessage(canonical)
20+
if err != nil {
21+
return nil, err
22+
}
23+
1224
ids := k.PublicData().IDs()
13-
signatures := make([]data.Signature, 0, len(s.Signatures)+1)
14-
for _, sig := range s.Signatures {
15-
found := false
16-
for _, id := range ids {
17-
if sig.KeyID == id {
18-
found = true
19-
break
20-
}
21-
}
22-
if !found {
23-
signatures = append(signatures, sig)
24-
}
25+
signatures := make([]data.Signature, 0, len(ids))
26+
for _, id := range ids {
27+
signatures = append(signatures, data.Signature{
28+
KeyID: id,
29+
Signature: sigData,
30+
})
2531
}
2632

33+
return signatures, nil
34+
}
35+
36+
// Sign signs the to-be-signed part of s using the signer k.
37+
//
38+
// The new signature(s) (one for each of k's key IDs) are appended to
39+
// s.Signatures. Existing signatures for the Key IDs are replaced.
40+
func Sign(s *data.Signed, k keys.Signer) error {
2741
canonical, err := cjson.EncodeCanonical(s.Signed)
2842
if err != nil {
2943
return err
3044
}
3145

32-
sig, err := k.SignMessage(canonical)
46+
size := len(s.Signatures)
47+
if size > maxSignatures-1 {
48+
return errors.New("value too large")
49+
}
50+
signatures := make([]data.Signature, 0, size+1)
51+
for _, oldSig := range s.Signatures {
52+
if !k.PublicData().ContainsID(oldSig.KeyID) {
53+
signatures = append(signatures, oldSig)
54+
}
55+
}
56+
57+
newSigs, err := MakeSignatures(canonical, k)
3358
if err != nil {
3459
return err
3560
}
61+
signatures = append(signatures, newSigs...)
3662

3763
s.Signatures = signatures
38-
for _, id := range ids {
39-
s.Signatures = append(s.Signatures, data.Signature{
40-
KeyID: id,
41-
Signature: sig,
42-
})
43-
}
44-
4564
return nil
4665
}
4766

0 commit comments

Comments
 (0)