mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 01:53:42 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			172 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			172 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package data
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
 | 
						|
	"github.com/docker/go/canonical/json"
 | 
						|
)
 | 
						|
 | 
						|
// SignedRoot is a fully unpacked root.json
 | 
						|
type SignedRoot struct {
 | 
						|
	Signatures []Signature
 | 
						|
	Signed     Root
 | 
						|
	Dirty      bool
 | 
						|
}
 | 
						|
 | 
						|
// Root is the Signed component of a root.json
 | 
						|
type Root struct {
 | 
						|
	SignedCommon
 | 
						|
	Keys               Keys                   `json:"keys"`
 | 
						|
	Roles              map[RoleName]*RootRole `json:"roles"`
 | 
						|
	ConsistentSnapshot bool                   `json:"consistent_snapshot"`
 | 
						|
}
 | 
						|
 | 
						|
// isValidRootStructure returns an error, or nil, depending on whether the content of the struct
 | 
						|
// is valid for root metadata.  This does not check signatures or expiry, just that
 | 
						|
// the metadata content is valid.
 | 
						|
func isValidRootStructure(r Root) error {
 | 
						|
	expectedType := TUFTypes[CanonicalRootRole]
 | 
						|
	if r.Type != expectedType {
 | 
						|
		return ErrInvalidMetadata{
 | 
						|
			role: CanonicalRootRole, msg: fmt.Sprintf("expected type %s, not %s", expectedType, r.Type)}
 | 
						|
	}
 | 
						|
 | 
						|
	if r.Version < 1 {
 | 
						|
		return ErrInvalidMetadata{
 | 
						|
			role: CanonicalRootRole, msg: "version cannot be less than 1"}
 | 
						|
	}
 | 
						|
 | 
						|
	// all the base roles MUST appear in the root.json - other roles are allowed,
 | 
						|
	// but other than the mirror role (not currently supported) are out of spec
 | 
						|
	for _, roleName := range BaseRoles {
 | 
						|
		roleObj, ok := r.Roles[roleName]
 | 
						|
		if !ok || roleObj == nil {
 | 
						|
			return ErrInvalidMetadata{
 | 
						|
				role: CanonicalRootRole, msg: fmt.Sprintf("missing %s role specification", roleName)}
 | 
						|
		}
 | 
						|
		if err := isValidRootRoleStructure(CanonicalRootRole, roleName, *roleObj, r.Keys); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func isValidRootRoleStructure(metaContainingRole, rootRoleName RoleName, r RootRole, validKeys Keys) error {
 | 
						|
	if r.Threshold < 1 {
 | 
						|
		return ErrInvalidMetadata{
 | 
						|
			role: metaContainingRole,
 | 
						|
			msg:  fmt.Sprintf("invalid threshold specified for %s: %v ", rootRoleName, r.Threshold),
 | 
						|
		}
 | 
						|
	}
 | 
						|
	for _, keyID := range r.KeyIDs {
 | 
						|
		if _, ok := validKeys[keyID]; !ok {
 | 
						|
			return ErrInvalidMetadata{
 | 
						|
				role: metaContainingRole,
 | 
						|
				msg:  fmt.Sprintf("key ID %s specified in %s without corresponding key", keyID, rootRoleName),
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// NewRoot initializes a new SignedRoot with a set of keys, roles, and the consistent flag
 | 
						|
func NewRoot(keys map[string]PublicKey, roles map[RoleName]*RootRole, consistent bool) (*SignedRoot, error) {
 | 
						|
	signedRoot := &SignedRoot{
 | 
						|
		Signatures: make([]Signature, 0),
 | 
						|
		Signed: Root{
 | 
						|
			SignedCommon: SignedCommon{
 | 
						|
				Type:    TUFTypes[CanonicalRootRole],
 | 
						|
				Version: 0,
 | 
						|
				Expires: DefaultExpires(CanonicalRootRole),
 | 
						|
			},
 | 
						|
			Keys:               keys,
 | 
						|
			Roles:              roles,
 | 
						|
			ConsistentSnapshot: consistent,
 | 
						|
		},
 | 
						|
		Dirty: true,
 | 
						|
	}
 | 
						|
 | 
						|
	return signedRoot, nil
 | 
						|
}
 | 
						|
 | 
						|
// BuildBaseRole returns a copy of a BaseRole using the information in this SignedRoot for the specified role name.
 | 
						|
// Will error for invalid role name or key metadata within this SignedRoot
 | 
						|
func (r SignedRoot) BuildBaseRole(roleName RoleName) (BaseRole, error) {
 | 
						|
	roleData, ok := r.Signed.Roles[roleName]
 | 
						|
	if !ok {
 | 
						|
		return BaseRole{}, ErrInvalidRole{Role: roleName, Reason: "role not found in root file"}
 | 
						|
	}
 | 
						|
	// Get all public keys for the base role from TUF metadata
 | 
						|
	keyIDs := roleData.KeyIDs
 | 
						|
	pubKeys := make(map[string]PublicKey)
 | 
						|
	for _, keyID := range keyIDs {
 | 
						|
		pubKey, ok := r.Signed.Keys[keyID]
 | 
						|
		if !ok {
 | 
						|
			return BaseRole{}, ErrInvalidRole{
 | 
						|
				Role:   roleName,
 | 
						|
				Reason: fmt.Sprintf("key with ID %s was not found in root metadata", keyID),
 | 
						|
			}
 | 
						|
		}
 | 
						|
		pubKeys[keyID] = pubKey
 | 
						|
	}
 | 
						|
 | 
						|
	return BaseRole{
 | 
						|
		Name:      roleName,
 | 
						|
		Keys:      pubKeys,
 | 
						|
		Threshold: roleData.Threshold,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
// ToSigned partially serializes a SignedRoot for further signing
 | 
						|
func (r SignedRoot) ToSigned() (*Signed, error) {
 | 
						|
	s, err := defaultSerializer.MarshalCanonical(r.Signed)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	// cast into a json.RawMessage
 | 
						|
	signed := json.RawMessage{}
 | 
						|
	err = signed.UnmarshalJSON(s)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	sigs := make([]Signature, len(r.Signatures))
 | 
						|
	copy(sigs, r.Signatures)
 | 
						|
	return &Signed{
 | 
						|
		Signatures: sigs,
 | 
						|
		Signed:     &signed,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
// MarshalJSON returns the serialized form of SignedRoot as bytes
 | 
						|
func (r SignedRoot) MarshalJSON() ([]byte, error) {
 | 
						|
	signed, err := r.ToSigned()
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return defaultSerializer.Marshal(signed)
 | 
						|
}
 | 
						|
 | 
						|
// RootFromSigned fully unpacks a Signed object into a SignedRoot and ensures
 | 
						|
// that it is a valid SignedRoot
 | 
						|
func RootFromSigned(s *Signed) (*SignedRoot, error) {
 | 
						|
	r := Root{}
 | 
						|
	if s.Signed == nil {
 | 
						|
		return nil, ErrInvalidMetadata{
 | 
						|
			role: CanonicalRootRole,
 | 
						|
			msg:  "root file contained an empty payload",
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if err := defaultSerializer.Unmarshal(*s.Signed, &r); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if err := isValidRootStructure(r); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	sigs := make([]Signature, len(s.Signatures))
 | 
						|
	copy(sigs, s.Signatures)
 | 
						|
	return &SignedRoot{
 | 
						|
		Signatures: sigs,
 | 
						|
		Signed:     r,
 | 
						|
	}, nil
 | 
						|
}
 |