blob: 2be5ee281417eaa1a8ce745daa11307de0240667 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package repo
import (
"encoding/json"
"fmt"
"strings"
tuf_data "github.com/theupdateframework/go-tuf/data"
)
// Config is a struct that mirrors an associated FIDL table
// definition in //sdk/fidl/fuchsia.pkg/repo.fidl. Documentation is as in
// that file. Ditto for the types that comprise its definition.
//
// Keep these in sync with their repo.fidl counterparts as well as the custom
//
type Config struct {
URL string `json:"repo_url"`
RootKeys []KeyConfig `json:"root_keys"`
Mirrors []MirrorConfig `json:"mirrors"`
RootVersion uint32 `json:"root_version"`
RootThreshold uint32 `json:"root_threshold"`
UpdatePackageURL string `json:"update_package_url,omitempty"`
UseLocalMirror bool `json:"use_local_mirror,omitempty"`
StorageType RepositoryStorageType `json:"storage_type,omitempty"`
}
type MirrorConfig struct {
URL string `json:"mirror_url"`
Subscribe bool `json:"subscribe"`
BlobURL string `json:"blob_mirror_url,omitempty"`
}
type KeyConfig struct {
// ED25519Key is a 32-byte, lowercase, hex-encoded key.
ED25519Key string
}
// The underlying type of the FIDL RepositoryStorageType is uint32,
// see //sdk/fidl/fuchsia.pkg/repo.fidl and
// https://fuchsia.dev/fuchsia-src/reference/fidl/language/language#enums
type RepositoryStorageType uint32
const (
Unset RepositoryStorageType = iota
Ephemeral
Persistent
)
func (t RepositoryStorageType) MarshalJSON() ([]byte, error) {
var s string
switch t {
case Unset:
// s is empty and dropped during marshaling due to "omitempty"
case Persistent:
s = "persistent"
case Ephemeral:
s = "ephemeral"
default:
return []byte{}, fmt.Errorf("unknown repository storage type: %q", t)
}
return json.Marshal(s)
}
func (t *RepositoryStorageType) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
switch strings.ToLower(s) {
case "persistent":
*t = Persistent
case "ephemeral":
*t = Ephemeral
default:
return fmt.Errorf("unknown repository storage type: %q", s)
}
return nil
}
// We replicate the serialization/deserialization logic given in
// //src/sys/pkg/lib/fidl-fuchsia-pkg-ext/src/repo.rs;
// Per this logic, we set the BlobURL field in MirrorConfig as omitempty (above),
// and give custom marshaling logic to the key config.
//
// This alias allows to make use of the default (un)marshalling logic of Config as we redefine it.
type config Config
func (cfg *Config) MarshalJSON() ([]byte, error) {
cfg2 := config(*cfg)
if cfg2.RootVersion == 0 {
cfg2.RootVersion = 1
}
if cfg2.RootThreshold == 0 {
cfg2.RootThreshold = 1
}
return json.Marshal(&cfg2)
}
func (cfg *Config) UnmarshalJSON(data []byte) error {
var cfg2 config
if err := json.Unmarshal(data, &cfg2); err != nil {
return err
}
if cfg2.RootVersion == 0 {
cfg2.RootVersion = 1
}
if cfg2.RootThreshold == 0 {
cfg2.RootThreshold = 1
}
*cfg = Config(cfg2)
return nil
}
type typeAndValue struct {
Type string `json:"type"`
Value string `json:"value"`
}
func (key *KeyConfig) MarshalJSON() ([]byte, error) {
return json.Marshal(&typeAndValue{
Type: tuf_data.KeyTypeEd25519,
Value: key.ED25519Key,
})
}
func (key *KeyConfig) UnmarshalJSON(data []byte) error {
var tv typeAndValue
if err := json.Unmarshal(data, &tv); err != nil {
return err
}
switch tv.Type {
case tuf_data.KeyTypeEd25519:
key.ED25519Key = tv.Value
return nil
default:
return fmt.Errorf("unexpected key type: %q", tv.Type)
}
}
// GetRootKeys returns the list of public key config objects as read from the
// contents of a repository's root metadata file.
func GetRootKeys(root *tuf_data.Root) ([]KeyConfig, error) {
rootKeys := make(map[string]struct{})
if role, ok := root.Roles["root"]; ok {
for _, id := range role.KeyIDs {
if key, ok := root.Keys[id]; ok {
switch key.Type {
case tuf_data.KeyTypeEd25519:
var kv struct {
Public tuf_data.HexBytes `json:"public"`
}
if err := json.Unmarshal(key.Value, &kv); err != nil {
return nil, fmt.Errorf("failed to unmarshal key: %w", err)
}
rootKeys[kv.Public.String()] = struct{}{}
default:
return nil, fmt.Errorf("unexpected key type: %q", key.Type)
}
}
}
}
rootKeyConfigs := make([]KeyConfig, 0, len(rootKeys))
for key := range rootKeys {
rootKeyConfigs = append(rootKeyConfigs, KeyConfig{ED25519Key: key})
}
return rootKeyConfigs, nil
}