forked from TrueCloudLab/distribution
76624704c3
To ensure that we only unmarshal the verified payload into the contained manifest, we first copy the entire incoming buffer into Raw and then unmarshal only the Payload portion of the incoming bytes. If the contents is later verified, the caller can then be sure that the contents of the Manifest fields can be trusted. Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
130 lines
3.7 KiB
Go
130 lines
3.7 KiB
Go
package schema1
|
|
|
|
import (
|
|
"encoding/json"
|
|
|
|
"github.com/docker/distribution/digest"
|
|
"github.com/docker/distribution/manifest"
|
|
"github.com/docker/libtrust"
|
|
)
|
|
|
|
// TODO(stevvooe): When we rev the manifest format, the contents of this
|
|
// package should be moved to manifest/v1.
|
|
|
|
const (
|
|
// ManifestMediaType specifies the mediaType for the current version. Note
|
|
// that for schema version 1, the the media is optionally
|
|
// "application/json".
|
|
ManifestMediaType = "application/vnd.docker.distribution.manifest.v1+json"
|
|
)
|
|
|
|
var (
|
|
// SchemaVersion provides a pre-initialized version structure for this
|
|
// packages version of the manifest.
|
|
SchemaVersion = manifest.Versioned{
|
|
SchemaVersion: 1,
|
|
}
|
|
)
|
|
|
|
// Manifest provides the base accessible fields for working with V2 image
|
|
// format in the registry.
|
|
type Manifest struct {
|
|
manifest.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 []History `json:"history"`
|
|
}
|
|
|
|
// 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:"-"`
|
|
}
|
|
|
|
// UnmarshalJSON populates a new ImageManifest struct from JSON data.
|
|
func (sm *SignedManifest) UnmarshalJSON(b []byte) error {
|
|
sm.Raw = make([]byte, len(b), len(b))
|
|
copy(sm.Raw, b)
|
|
|
|
p, err := sm.Payload()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var manifest Manifest
|
|
if err := json.Unmarshal(p, &manifest); err != nil {
|
|
return err
|
|
}
|
|
|
|
sm.Manifest = manifest
|
|
return nil
|
|
}
|
|
|
|
// Payload returns the raw, signed content of the signed manifest. The
|
|
// contents can be used to calculate the content identifier.
|
|
func (sm *SignedManifest) Payload() ([]byte, error) {
|
|
jsig, err := libtrust.ParsePrettySignature(sm.Raw, "signatures")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Resolve the payload in the manifest.
|
|
return jsig.Payload()
|
|
}
|
|
|
|
// Signatures returns the signatures as provided by
|
|
// (*libtrust.JSONSignature).Signatures. The byte slices are opaque jws
|
|
// signatures.
|
|
func (sm *SignedManifest) Signatures() ([][]byte, error) {
|
|
jsig, err := libtrust.ParsePrettySignature(sm.Raw, "signatures")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Resolve the payload in the manifest.
|
|
return jsig.Signatures()
|
|
}
|
|
|
|
// 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 be
|
|
// 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"`
|
|
}
|
|
|
|
// History stores unstructured v1 compatibility information
|
|
type History struct {
|
|
// V1Compatibility is the raw v1 compatibility information
|
|
V1Compatibility string `json:"v1Compatibility"`
|
|
}
|