Skip to content

Commit 355e39c

Browse files
authored
feat: Implement TAP-12 support (#310)
* docs: Add DCO instructions Signed-off-by: Zachary Newman <[email protected]> * feat: implement TAP-12 support Main changes: - allow IDs that aren't the SHA2 of the public key - but disallow multiple distinct keys with the same ID - test for TAP-12 compliance - Adding keys should disallow different keys with the same ID, but allow everything else - Verification should ensure that we have unique keys for each signature Fixes #232. Signed-off-by: Zachary Newman <[email protected]> * feat: add Key ID to ErrRepeatID Signed-off-by: Zachary Newman <[email protected]> * test: add test for keyIDs case for delegations DB Signed-off-by: Zachary Newman <[email protected]> * test: clarify DB tests for TAP-12 Signed-off-by: Zachary Newman <[email protected]>
1 parent ae904d2 commit 355e39c

File tree

5 files changed

+101
-41
lines changed

5 files changed

+101
-41
lines changed

client/client.go

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -499,15 +499,7 @@ func (c *Client) loadAndVerifyRootMeta(rootJSON []byte, ignoreExpiredCheck bool)
499499
ndb := verify.NewDB()
500500
for id, k := range root.Keys {
501501
if err := ndb.AddKey(id, k); err != nil {
502-
// TUF is considering in TAP-12 removing the
503-
// requirement that the keyid hash algorithm be derived
504-
// from the public key. So to be forwards compatible,
505-
// we ignore `ErrWrongID` errors.
506-
//
507-
// TAP-12: https://github.com/theupdateframework/taps/blob/master/tap12.md
508-
if _, ok := err.(verify.ErrWrongID); !ok {
509-
return err
510-
}
502+
return err
511503
}
512504
}
513505
for name, role := range root.Roles {
@@ -555,15 +547,7 @@ func (c *Client) verifyRoot(aJSON []byte, bJSON []byte) (*data.Root, error) {
555547
ndb := verify.NewDB()
556548
for id, k := range aRoot.Keys {
557549
if err := ndb.AddKey(id, k); err != nil {
558-
// TUF is considering in TAP-12 removing the
559-
// requirement that the keyid hash algorithm be derived
560-
// from the public key. So to be forwards compatible,
561-
// we ignore `ErrWrongID` errors.
562-
//
563-
// TAP-12: https://github.com/theupdateframework/taps/blob/master/tap12.md
564-
if _, ok := err.(verify.ErrWrongID); !ok {
565-
return nil, err
566-
}
550+
return nil, err
567551
}
568552
}
569553
for name, role := range aRoot.Roles {

repo.go

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -103,15 +103,7 @@ func (r *Repo) topLevelKeysDB() (*verify.DB, error) {
103103
}
104104
for id, k := range root.Keys {
105105
if err := db.AddKey(id, k); err != nil {
106-
// TUF is considering in TAP-12 removing the
107-
// requirement that the keyid hash algorithm be derived
108-
// from the public key. So to be forwards compatible,
109-
// we ignore `ErrWrongID` errors.
110-
//
111-
// TAP-12: https://github.com/theupdateframework/taps/blob/master/tap12.md
112-
if _, ok := err.(verify.ErrWrongID); !ok {
113-
return nil, err
114-
}
106+
return nil, err
115107
}
116108
}
117109
for name, role := range root.Roles {

verify/db.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,23 @@ func NewDBFromDelegations(d *data.Delegations) (*DB, error) {
5353
}
5454

5555
func (db *DB) AddKey(id string, k *data.PublicKey) error {
56-
if !k.ContainsID(id) {
57-
return ErrWrongID{}
58-
}
5956
verifier, err := keys.GetVerifier(k)
6057
if err != nil {
6158
return ErrInvalidKey
6259
}
60+
61+
// TUF is considering in TAP-12 removing the
62+
// requirement that the keyid hash algorithm be derived
63+
// from the public key. So to be forwards compatible,
64+
// we allow any key ID, rather than checking k.ContainsID(id)
65+
//
66+
// AddKey should be idempotent, so we allow re-adding the same PublicKey.
67+
//
68+
// TAP-12: https://github.com/theupdateframework/taps/blob/master/tap12.md
69+
if oldVerifier, exists := db.verifiers[id]; exists && oldVerifier.Public() != verifier.Public() {
70+
return ErrRepeatID{id}
71+
}
72+
6373
db.verifiers[id] = verifier
6474
return nil
6575
}
@@ -74,9 +84,6 @@ func (db *DB) AddRole(name string, r *data.Role) error {
7484
Threshold: r.Threshold,
7585
}
7686
for _, id := range r.KeyIDs {
77-
if len(id) != data.KeyIDLength {
78-
return ErrInvalidKeyID
79-
}
8087
role.KeyIDs[id] = struct{}{}
8188
}
8289

verify/db_test.go

Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ import (
55

66
"github.com/stretchr/testify/assert"
77
"github.com/theupdateframework/go-tuf/data"
8+
"github.com/theupdateframework/go-tuf/pkg/keys"
89
)
910

1011
func TestDelegationsDB(t *testing.T) {
12+
key, err := keys.GenerateEd25519Key()
13+
assert.Nil(t, err, "generating key failed")
1114
var dbTests = []struct {
1215
testName string
1316
delegations *data.Delegations
@@ -34,11 +37,40 @@ func TestDelegationsDB(t *testing.T) {
3437
initErr: ErrInvalidThreshold,
3538
},
3639
{
37-
testName: "invalid keys",
38-
delegations: &data.Delegations{Keys: map[string]*data.PublicKey{
39-
"a": {Type: data.KeySchemeEd25519},
40-
}},
41-
initErr: ErrWrongID{},
40+
testName: "standard (SHA256) key IDs supported",
41+
delegations: &data.Delegations{
42+
Keys: map[string]*data.PublicKey{
43+
key.PublicData().IDs()[0]: key.PublicData(),
44+
},
45+
Roles: []data.DelegatedRole{{
46+
Name: "rolename",
47+
KeyIDs: key.PublicData().IDs(),
48+
Threshold: 1,
49+
},
50+
},
51+
},
52+
// If we get to ErrNoSignatures, we've passed key loading; see
53+
// delegations_test.go to see tests that delegation DB *fully* works
54+
// with valid signatures set up.
55+
unmarshalErr: ErrNoSignatures,
56+
},
57+
{
58+
testName: "arbitrary (non-SHA256, per TAP-12) key IDs supported",
59+
delegations: &data.Delegations{
60+
Keys: map[string]*data.PublicKey{
61+
"a": key.PublicData(),
62+
},
63+
Roles: []data.DelegatedRole{{
64+
Name: "rolename",
65+
KeyIDs: []string{"a"},
66+
Threshold: 1,
67+
},
68+
},
69+
},
70+
// If we get to ErrNoSignatures, we've passed key loading; see
71+
// delegations_test.go to see tests that delegation DB *fully* works
72+
// with valid signatures set up.
73+
unmarshalErr: ErrNoSignatures,
4274
},
4375
}
4476

@@ -55,3 +87,46 @@ func TestDelegationsDB(t *testing.T) {
5587
})
5688
}
5789
}
90+
91+
// Test key database for compliance with TAP-12.
92+
//
93+
// Previously, every key's key ID was the SHA256 of the public key. TAP-12
94+
// allows arbitrary key IDs, with no loss in security.
95+
//
96+
//
97+
// TAP-12: https://github.com/theupdateframework/taps/blob/master/tap12.md
98+
func TestTAP12(t *testing.T) {
99+
db := NewDB()
100+
// Need to use a signer type that supports random signatures.
101+
key1, _ := keys.GenerateRsaKey()
102+
key2, _ := keys.GenerateRsaKey()
103+
msg := []byte("{}")
104+
sig1, _ := key1.SignMessage(msg)
105+
sig1Duplicate, _ := key1.SignMessage(msg)
106+
assert.NotEqual(t, sig1, sig1Duplicate, "Signatures should be random")
107+
sig2, _ := key2.SignMessage(msg)
108+
109+
// Idempotent: adding the same key with the same ID is okay.
110+
assert.Nil(t, db.AddKey("key1", key1.PublicData()), "initial add")
111+
assert.Nil(t, db.AddKey("key1", key1.PublicData()), "re-add")
112+
// Adding a different key is allowed, unless the key ID is the same.
113+
assert.Nil(t, db.AddKey("key2", key2.PublicData()), "different key with different ID")
114+
assert.ErrorIs(t, db.AddKey("key1", key2.PublicData()), ErrRepeatID{"key1"}, "different key with same key ID")
115+
assert.Nil(t, db.AddKey("key1-duplicate", key1.PublicData()), "same key with different ID should succeed")
116+
assert.Nil(t, db.AddRole("diffkeys", &data.Role{
117+
KeyIDs: []string{"key1", "key2"},
118+
Threshold: 2,
119+
}), "adding role")
120+
assert.Nil(t, db.AddRole("samekeys", &data.Role{
121+
KeyIDs: []string{"key1", "key1-alt"},
122+
Threshold: 2,
123+
}), "adding role")
124+
assert.Nil(t, db.VerifySignatures(&data.Signed{
125+
Signed: msg,
126+
Signatures: []data.Signature{{KeyID: "key1", Signature: sig1}, {KeyID: "key2", Signature: sig2}},
127+
}, "diffkeys"), "Signature with different keys: ")
128+
assert.ErrorIs(t, db.VerifySignatures(&data.Signed{
129+
Signed: msg,
130+
Signatures: []data.Signature{{KeyID: "key1", Signature: sig1}, {KeyID: "key1-alt", Signature: sig1Duplicate}},
131+
}, "samekeys"), ErrRoleThreshold{2, 1}, "Threshold signing with repeat key")
132+
}

verify/errors.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@ var (
2121
ErrMissingTargetFile = errors.New("tuf: missing previously listed targets metadata file")
2222
)
2323

24-
type ErrWrongID struct{}
24+
type ErrRepeatID struct {
25+
KeyID string
26+
}
2527

26-
func (ErrWrongID) Error() string {
27-
return "tuf: key id mismatch"
28+
func (e ErrRepeatID) Error() string {
29+
return fmt.Sprintf("tuf: duplicate key id (%s)", e.KeyID)
2830
}
2931

3032
type ErrUnknownRole struct {

0 commit comments

Comments
 (0)