1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
package main
import (
"encoding/json"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"time"
tuf "github.com/theupdateframework/go-tuf"
"github.com/theupdateframework/go-tuf/data"
"github.com/theupdateframework/go-tuf/pkg/keys"
)
var expirationDate = time.Date(2100, time.January, 1, 0, 0, 0, 0, time.UTC)
type persistedKeys struct {
Encrypted bool `json:"encrypted"`
Data []*data.PrivateKey `json:"data"`
}
func assertNoError(err error) {
if err != nil {
panic(fmt.Sprintf("assertion failed: %s", err))
}
}
func copyRepo(src string, dst string) {
cmd := exec.Command("cp", "-r", src, dst)
assertNoError(cmd.Run())
}
func newRepo(dir string) *tuf.Repo {
repo, err := tuf.NewRepoIndent(tuf.FileSystemStore(dir, nil), "", "\t")
assertNoError(err)
return repo
}
func commit(dir string, repo *tuf.Repo) {
assertNoError(repo.SnapshotWithExpires(expirationDate))
assertNoError(repo.TimestampWithExpires(expirationDate))
assertNoError(repo.Commit())
// Remove the keys directory to make sure we don't accidentally use a key.
assertNoError(os.RemoveAll(filepath.Join(dir, "keys")))
}
func addKeys(repo *tuf.Repo, roleKeys map[string][]*data.PrivateKey) {
for role, keyList := range roleKeys {
for _, key := range keyList {
signer, err := keys.GetSigner(key)
assertNoError(err)
assertNoError(repo.AddPrivateKeyWithExpires(role, signer, expirationDate))
}
}
}
func addTargets(repo *tuf.Repo, dir string, files map[string][]byte) {
paths := []string{}
for file, data := range files {
path := filepath.Join(dir, "staged", "targets", file)
assertNoError(os.MkdirAll(filepath.Dir(path), 0755))
assertNoError(os.WriteFile(path, data, 0644))
paths = append(paths, file)
}
assertNoError(repo.AddTargetsWithExpires(paths, nil, expirationDate))
}
func revokeKeys(repo *tuf.Repo, role string, keyList []*data.PrivateKey) {
for _, key := range keyList {
signer, err := keys.GetSigner(key)
assertNoError(err)
assertNoError(repo.RevokeKeyWithExpires(role, signer.PublicData().IDs()[0], expirationDate))
}
}
func generateRepos(dir string, consistentSnapshot bool) {
f, err := os.Open("../keys.json")
assertNoError(err)
var roleKeys map[string][][]*data.PrivateKey
assertNoError(json.NewDecoder(f).Decode(&roleKeys))
// Collect all the initial keys we'll use when creating repositories.
// We'll modify this to reflect rotated keys.
keys := map[string][]*data.PrivateKey{
"root": roleKeys["root"][0],
"targets": roleKeys["targets"][0],
"snapshot": roleKeys["snapshot"][0],
"timestamp": roleKeys["timestamp"][0],
}
// Create the initial repo.
dir0 := filepath.Join(dir, "0")
repo0 := newRepo(dir0)
repo0.Init(consistentSnapshot)
addKeys(repo0, keys)
addTargets(repo0, dir0, map[string][]byte{"0": []byte("0")})
commit(dir0, repo0)
// Rotate all the keys to make sure that works.
oldDir := dir0
i := 1
for _, role := range []string{"root", "targets", "snapshot", "timestamp"} {
// Setup the repo.
stepName := fmt.Sprintf("%d", i)
d := filepath.Join(dir, stepName)
copyRepo(oldDir, d)
repo := newRepo(d)
addKeys(repo, keys)
// Actually rotate the keys
revokeKeys(repo, role, roleKeys[role][0])
addKeys(repo, map[string][]*data.PrivateKey{
role: roleKeys[role][1],
})
keys[role] = roleKeys[role][1]
// Add a target to make sure that works, then commit.
addTargets(repo, d, map[string][]byte{stepName: []byte(stepName)})
commit(d, repo)
i += 1
oldDir = d
}
// Add another target file to make sure the workflow worked.
stepName := fmt.Sprintf("%d", i)
d := filepath.Join(dir, stepName)
copyRepo(oldDir, d)
repo := newRepo(d)
addKeys(repo, keys)
addTargets(repo, d, map[string][]byte{stepName: []byte(stepName)})
commit(d, repo)
}
func main() {
cwd, err := os.Getwd()
assertNoError(err)
for _, consistentSnapshot := range []bool{false, true} {
name := fmt.Sprintf("consistent-snapshot-%t", consistentSnapshot)
log.Printf("generating %s", name)
generateRepos(filepath.Join(cwd, name), consistentSnapshot)
}
}
|