| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- // Unless explicitly stated otherwise all files in this repository are licensed
- // under the Apache License Version 2.0.
- // This product includes software developed at Datadog (https://www.datadoghq.com/).
- // Copyright 2022-present Datadog, Inc.
- package state
- import (
- "bytes"
- "encoding/json"
- "fmt"
- "io"
- "strconv"
- "strings"
- "github.com/DataDog/go-tuf/client"
- "github.com/DataDog/go-tuf/data"
- "github.com/DataDog/go-tuf/util"
- "github.com/DataDog/go-tuf/verify"
- )
- type tufRootsClient struct {
- rootClient *client.Client
- rootLocalStore client.LocalStore
- rootRemoteStore *rootClientRemoteStore
- }
- func newTufRootsClient(root []byte) (*tufRootsClient, error) {
- rootLocalStore := client.MemoryLocalStore()
- rootRemoteStore := &rootClientRemoteStore{}
- rootClient := client.NewClient(rootLocalStore, rootRemoteStore)
- err := rootClient.InitLocal(root)
- if err != nil {
- return nil, err
- }
- return &tufRootsClient{
- rootClient: rootClient,
- rootLocalStore: rootLocalStore,
- rootRemoteStore: rootRemoteStore,
- }, nil
- }
- func (trc *tufRootsClient) clone() (*tufRootsClient, error) {
- root, err := trc.latestRootRaw()
- if err != nil {
- return nil, err
- }
- return newTufRootsClient(root)
- }
- func (trc *tufRootsClient) updateRoots(newRoots [][]byte) error {
- if len(newRoots) == 0 {
- return nil
- }
- trc.rootRemoteStore.roots = append(trc.rootRemoteStore.roots, newRoots...)
- return trc.rootClient.UpdateRoots()
- }
- func (trc *tufRootsClient) latestRoot() (*data.Root, error) {
- raw, err := trc.latestRootRaw()
- if err != nil {
- return nil, err
- }
- return unsafeUnmarshalRoot(raw)
- }
- func (trc *tufRootsClient) latestRootRaw() ([]byte, error) {
- metas, err := trc.rootLocalStore.GetMeta()
- if err != nil {
- return nil, err
- }
- rawRoot := metas["root.json"]
- return rawRoot, nil
- }
- func (trc *tufRootsClient) validateTargets(rawTargets []byte) (*data.Targets, error) {
- root, err := trc.latestRoot()
- if err != nil {
- return nil, err
- }
- db := verify.NewDB()
- for _, key := range root.Keys {
- for _, id := range key.IDs() {
- if err := db.AddKey(id, key); err != nil {
- return nil, err
- }
- }
- }
- targetsRole, hasRoleTargets := root.Roles["targets"]
- if !hasRoleTargets {
- return nil, fmt.Errorf("root is missing a targets role")
- }
- role := &data.Role{Threshold: targetsRole.Threshold, KeyIDs: targetsRole.KeyIDs}
- if err := db.AddRole("targets", role); err != nil {
- return nil, fmt.Errorf("could not add targets role to db: %v", err)
- }
- var targets data.Targets
- err = db.Unmarshal(rawTargets, &targets, "targets", 0)
- if err != nil {
- return nil, err
- }
- return &targets, nil
- }
- type rootClientRemoteStore struct {
- roots [][]byte
- }
- func (s *rootClientRemoteStore) GetMeta(name string) (stream io.ReadCloser, size int64, err error) {
- metaPath, err := parseMetaPath(name)
- if err != nil {
- return nil, 0, err
- }
- if metaPath.role != roleRoot || !metaPath.versionSet {
- return nil, 0, client.ErrNotFound{File: name}
- }
- for _, root := range s.roots {
- parsedRoot, err := unsafeUnmarshalRoot(root)
- if err != nil {
- return nil, 0, err
- }
- if parsedRoot.Version == metaPath.version {
- return io.NopCloser(bytes.NewReader(root)), int64(len(root)), nil
- }
- }
- return nil, 0, client.ErrNotFound{File: name}
- }
- func (s *rootClientRemoteStore) GetTarget(path string) (stream io.ReadCloser, size int64, err error) {
- return nil, 0, client.ErrNotFound{File: path}
- }
- type role string
- const (
- roleRoot role = "root"
- )
- type metaPath struct {
- role role
- version int64
- versionSet bool
- }
- func parseMetaPath(rawMetaPath string) (metaPath, error) {
- splitRawMetaPath := strings.SplitN(rawMetaPath, ".", 3)
- if len(splitRawMetaPath) != 2 && len(splitRawMetaPath) != 3 {
- return metaPath{}, fmt.Errorf("invalid metadata path '%s'", rawMetaPath)
- }
- suffix := splitRawMetaPath[len(splitRawMetaPath)-1]
- if suffix != "json" {
- return metaPath{}, fmt.Errorf("invalid metadata path (suffix) '%s'", rawMetaPath)
- }
- rawRole := splitRawMetaPath[len(splitRawMetaPath)-2]
- if rawRole == "" {
- return metaPath{}, fmt.Errorf("invalid metadata path (role) '%s'", rawMetaPath)
- }
- if len(splitRawMetaPath) == 2 {
- return metaPath{
- role: role(rawRole),
- }, nil
- }
- rawVersion, err := strconv.ParseInt(splitRawMetaPath[0], 10, 64)
- if err != nil {
- return metaPath{}, fmt.Errorf("invalid metadata path (version) '%s': %w", rawMetaPath, err)
- }
- return metaPath{
- role: role(rawRole),
- version: rawVersion,
- versionSet: true,
- }, nil
- }
- func validateTargetFileHash(targetMeta data.TargetFileMeta, targetFile []byte) error {
- if len(targetMeta.HashAlgorithms()) == 0 {
- return fmt.Errorf("target file has no hash")
- }
- generatedMeta, err := util.GenerateFileMeta(bytes.NewBuffer(targetFile), targetMeta.HashAlgorithms()...)
- if err != nil {
- return err
- }
- err = util.FileMetaEqual(targetMeta.FileMeta, generatedMeta)
- if err != nil {
- return err
- }
- return nil
- }
- func unsafeUnmarshalRoot(raw []byte) (*data.Root, error) {
- var signedRoot data.Signed
- err := json.Unmarshal(raw, &signedRoot)
- if err != nil {
- return nil, err
- }
- var root data.Root
- err = json.Unmarshal(signedRoot.Signed, &root)
- if err != nil {
- return nil, err
- }
- return &root, err
- }
- func unsafeUnmarshalTargets(raw []byte) (*data.Targets, error) {
- var signedTargets data.Signed
- err := json.Unmarshal(raw, &signedTargets)
- if err != nil {
- return nil, err
- }
- var targets data.Targets
- err = json.Unmarshal(signedTargets.Signed, &targets)
- if err != nil {
- return nil, err
- }
- return &targets, err
- }
- func extractRootVersion(raw []byte) (int64, error) {
- root, err := unsafeUnmarshalRoot(raw)
- if err != nil {
- return 0, err
- }
- return root.Version, nil
- }
|