Skip to content

Instantly share code, notes, and snippets.

@hiyosi
Last active September 18, 2024 23:06
Show Gist options
  • Save hiyosi/6fb4d0d77e31cd9637567dab4b02bafb to your computer and use it in GitHub Desktop.
Save hiyosi/6fb4d0d77e31cd9637567dab4b02bafb to your computer and use it in GitHub Desktop.
TUF

prepare repository

[p1]$ go run main.go -init --work-dir repo
flowchart TD
    Root --> Snapshot
    Root --> Timestamp
    Root --> Targets
    Targets -.-> potate.txt
    Targets -.-> foo/bar/baz.txt
    Targets --delegate--> my-role
    my-role -.-> my-role/apple.txt
    my-role -.-> my-role/banana.txt
 
Loading

prepare server

// listen :8080
[p2]$ go run server.go --work-dir repo

prepare client

[p3]$ go install  github.com/theupdateframework/go-tuf/cmd/tuf-client

[p3]$ mkdir client

// copy bootstrap root metadata
[p3]$ cp repo/repository/root.json client/. 

[p3]$ cd client
[p3]$ tuf-client init http://localhost:8080/ root.json  

// delegated contents is not displayed (Lack of functionality)
[p3]$ tuf-client list http://localhost:8080

// but you can get delegated contents (if you knew the name)
[p3]$ tuf-client get http://localhost:8080/ my-role/apple.txt
apples are sometimes red

update contents

[p1]$ go run main.go --work-dir repo

get updated content

[p2]$ tuf-client get http://localhost:8080/ my-role/apple.txt
xxx

arbitrary software installation attack

[p1]$ apple_hash=`cat repo/repository/my-role.json | jq -r '.signed.targets["my-role/apple.txt"].hashes.sha512'` 

[p1]$ echo "attack" > repo/repository/targets/my-role/${apple_hash}.apple.txt

[p3]$ tuf-client get http://localhost:8080/ my-role/apple.txt
ERROR: tuf: unexpected file size: my-role/apple.txt (expected 8 bytes, got 7 bytes)
package main
import (
"flag"
"net/http"
)
var (
workDir string
)
func init() {
flag.StringVar(&workDir, "work-dir", ".", "Path to directory to generate repository")
}
func main() {
flag.Parse()
http.ListenAndServe(":8080", http.FileServer(http.Dir(workDir)))
}
package main
import (
"flag"
"log"
"os"
"path/filepath"
"github.com/theupdateframework/go-tuf"
"github.com/theupdateframework/go-tuf/data"
"github.com/theupdateframework/go-tuf/pkg/keys"
"github.com/xyproto/randomstring"
)
var (
workDir string
initMode bool
)
func init() {
flag.StringVar(&workDir, "work-dir", ".", "Path to directory to generate repository")
flag.BoolVar(&initMode, "init", false, "If true, initialize TUF repository whereas false update the repository.")
}
func main() {
flag.Parse()
ls := tuf.FileSystemStore(workDir, nil)
r, err := tuf.NewRepo(ls)
if err != nil {
log.Fatalf("Failed to initialize the repository: %v", err)
}
if initMode {
log.Println("Initialize TUF repository")
initRepo(r, ls)
} else {
log.Println("Update TUF repository")
updateRepo(r, "my-role/apple.txt")
}
}
func initRepo(r *tuf.Repo, ls tuf.LocalStore) {
log.Println("Initialize top-level roles")
_, err := r.GenKey("root")
if err != nil {
log.Fatalf("Failed to generate the root role key: %v", err)
}
_, err = r.GenKey("targets")
if err != nil {
log.Fatalf("Failed to generate the targets role key: %v", err)
}
_, err = r.GenKey("snapshot")
if err != nil {
log.Fatalf("Failed to generate the snapshot role key: %v", err)
}
_, err = r.GenKey("timestamp")
if err != nil {
log.Fatalf("Failed to generate the timestamp role key: %v", err)
}
// The targets role delegates contents under my-role dir to the my-role role.
log.Println("Initialize delegations")
myRoleKey, err := keys.GenerateEd25519Key()
if err != nil {
log.Fatalf("Failed to generate the delegated role key: %v", err)
}
if err := ls.SaveSigner("my-role", myRoleKey); err != nil {
log.Fatalf("Failed to save signer: %v", err)
}
delegatedRole := data.DelegatedRole{
Name: "my-role",
KeyIDs: myRoleKey.PublicData().IDs(),
Paths: []string{"my-role/*"},
Threshold: 1,
}
if err := r.AddDelegatedRole("targets", delegatedRole, []*data.PublicKey{myRoleKey.PublicData()}); err != nil {
log.Fatalf("Failed to add the delegated role: %v", err)
}
log.Println("Add some targets")
files := map[string]string{
"potato.txt": "potatoes can be starchy or waxy",
"foo/bar/baz.txt": "foo, bar, baz",
"my-role/apple.txt": "apples are sometimes red",
"my-role/banana.txt": "bananas are yellow and sometimes brown",
}
for name, data := range files {
p := filepath.Join(workDir, "staged/targets", name)
os.MkdirAll(filepath.Dir(p), 0755)
os.WriteFile(p, []byte(data), 0644)
r.AddTarget(name, nil)
}
log.Println("Snapshot")
r.Snapshot()
log.Println("Timestamp")
r.Timestamp()
log.Println("Commit")
r.Commit()
}
func updateRepo(r *tuf.Repo, name string) {
p := filepath.Join(workDir, "staged/targets", name)
os.MkdirAll(filepath.Dir(p), 0755)
os.WriteFile(p, []byte(randomstring.HumanFriendlyEnglishString(8)), 0644)
r.AddTarget(name, nil)
log.Println("Snapshot")
r.Snapshot()
r.Timestamp()
// move contents from staged dir to repository dir
log.Println("Commit")
r.Commit()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment