174 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			174 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
package storage
 | 
						|
 | 
						|
import (
 | 
						|
	"crypto/x509"
 | 
						|
	"encoding/json"
 | 
						|
 | 
						|
	"github.com/Sirupsen/logrus"
 | 
						|
	"github.com/docker/docker-registry/digest"
 | 
						|
	"github.com/docker/libtrust"
 | 
						|
)
 | 
						|
 | 
						|
// Versioned provides a struct with just the manifest schemaVersion. Incoming
 | 
						|
// content with unknown schema version can be decoded against this struct to
 | 
						|
// check the version.
 | 
						|
type Versioned struct {
 | 
						|
	// SchemaVersion is the image manifest schema that this image follows
 | 
						|
	SchemaVersion int `json:"schemaVersion"`
 | 
						|
}
 | 
						|
 | 
						|
// Manifest provides the base accessible fields for working with V2 image
 | 
						|
// format in the registry.
 | 
						|
type Manifest struct {
 | 
						|
	Versioned
 | 
						|
 | 
						|
	// Name is the name of the image's repository
 | 
						|
	Name string `json:"name"`
 | 
						|
 | 
						|
	// Tag is the tag of the image specified by this manifest
 | 
						|
	Tag string `json:"tag"`
 | 
						|
 | 
						|
	// Architecture is the host architecture on which this image is intended to
 | 
						|
	// run
 | 
						|
	Architecture string `json:"architecture"`
 | 
						|
 | 
						|
	// FSLayers is a list of filesystem layer blobSums contained in this image
 | 
						|
	FSLayers []FSLayer `json:"fsLayers"`
 | 
						|
 | 
						|
	// History is a list of unstructured historical data for v1 compatibility
 | 
						|
	History []ManifestHistory `json:"history"`
 | 
						|
}
 | 
						|
 | 
						|
// Sign signs the manifest with the provided private key, returning a
 | 
						|
// SignedManifest. This typically won't be used within the registry, except
 | 
						|
// for testing.
 | 
						|
func (m *Manifest) Sign(pk libtrust.PrivateKey) (*SignedManifest, error) {
 | 
						|
	p, err := json.MarshalIndent(m, "", "   ")
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	js, err := libtrust.NewJSONSignature(p)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	if err := js.Sign(pk); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	pretty, err := js.PrettySignature("signatures")
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return &SignedManifest{
 | 
						|
		Manifest: *m,
 | 
						|
		Raw:      pretty,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
// SignWithChain signs the manifest with the given private key and x509 chain.
 | 
						|
// The public key of the first element in the chain must be the public key
 | 
						|
// corresponding with the sign key.
 | 
						|
func (m *Manifest) SignWithChain(key libtrust.PrivateKey, chain []*x509.Certificate) (*SignedManifest, error) {
 | 
						|
	p, err := json.MarshalIndent(m, "", "   ")
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	js, err := libtrust.NewJSONSignature(p)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	if err := js.SignWithChain(key, chain); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	pretty, err := js.PrettySignature("signatures")
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return &SignedManifest{
 | 
						|
		Manifest: *m,
 | 
						|
		Raw:      pretty,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
// SignedManifest provides an envelope for a signed image manifest, including
 | 
						|
// the format sensitive raw bytes. It contains fields to
 | 
						|
type SignedManifest struct {
 | 
						|
	Manifest
 | 
						|
 | 
						|
	// Raw is the byte representation of the ImageManifest, used for signature
 | 
						|
	// verification. The value of Raw must be used directly during
 | 
						|
	// serialization, or the signature check will fail. The manifest byte
 | 
						|
	// representation cannot change or it will have to be re-signed.
 | 
						|
	Raw []byte `json:"-"`
 | 
						|
}
 | 
						|
 | 
						|
// Verify verifies the signature of the signed manifest returning the public
 | 
						|
// keys used during signing.
 | 
						|
func (sm *SignedManifest) Verify() ([]libtrust.PublicKey, error) {
 | 
						|
	js, err := libtrust.ParsePrettySignature(sm.Raw, "signatures")
 | 
						|
	if err != nil {
 | 
						|
		logrus.WithField("err", err).Debugf("(*SignedManifest).Verify")
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return js.Verify()
 | 
						|
}
 | 
						|
 | 
						|
// VerifyChains verifies the signature of the signed manifest against the
 | 
						|
// certificate pool returning the list of verified chains. Signatures without
 | 
						|
// an x509 chain are not checked.
 | 
						|
func (sm *SignedManifest) VerifyChains(ca *x509.CertPool) ([][]*x509.Certificate, error) {
 | 
						|
	js, err := libtrust.ParsePrettySignature(sm.Raw, "signatures")
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return js.VerifyChains(ca)
 | 
						|
}
 | 
						|
 | 
						|
// UnmarshalJSON populates a new ImageManifest struct from JSON data.
 | 
						|
func (sm *SignedManifest) UnmarshalJSON(b []byte) error {
 | 
						|
	var manifest Manifest
 | 
						|
	if err := json.Unmarshal(b, &manifest); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	sm.Manifest = manifest
 | 
						|
	sm.Raw = make([]byte, len(b), len(b))
 | 
						|
	copy(sm.Raw, b)
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// MarshalJSON returns the contents of raw. If Raw is nil, marshals the inner
 | 
						|
// contents. Applications requiring a marshaled signed manifest should simply
 | 
						|
// use Raw directly, since the the content produced by json.Marshal will
 | 
						|
// compacted and will fail signature checks.
 | 
						|
func (sm *SignedManifest) MarshalJSON() ([]byte, error) {
 | 
						|
	if len(sm.Raw) > 0 {
 | 
						|
		return sm.Raw, nil
 | 
						|
	}
 | 
						|
 | 
						|
	// If the raw data is not available, just dump the inner content.
 | 
						|
	return json.Marshal(&sm.Manifest)
 | 
						|
}
 | 
						|
 | 
						|
// FSLayer is a container struct for BlobSums defined in an image manifest
 | 
						|
type FSLayer struct {
 | 
						|
	// BlobSum is the tarsum of the referenced filesystem image layer
 | 
						|
	BlobSum digest.Digest `json:"blobSum"`
 | 
						|
}
 | 
						|
 | 
						|
// ManifestHistory stores unstructured v1 compatibility information
 | 
						|
type ManifestHistory struct {
 | 
						|
	// V1Compatibility is the raw v1 compatibility information
 | 
						|
	V1Compatibility string `json:"v1Compatibility"`
 | 
						|
}
 |