From 5302569ce491eb693c7299adff844b9ef660d542 Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Wed, 10 May 2017 04:17:12 +0000 Subject: [PATCH] descriptor: sha{256,512}: disallow /[A-F]/ characters Signed-off-by: Akihiro Suda --- descriptor.md | 9 +++++++++ schema/descriptor_test.go | 3 ++- schema/validator.go | 41 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/descriptor.md b/descriptor.md index 697ad6055..7a879ef10 100644 --- a/descriptor.md +++ b/descriptor.md @@ -76,6 +76,9 @@ algorithm-separator := /[+._-]/ encoded := /[a-zA-Z0-9=_-]+/ ``` +Note that _algorithm_ MAY impose algorithm-specific restriction on the grammar of the _encoded_ portion. +See also [Registered Algorithms](#registered-identifiers). + Some example digest strings include the following: digest | algorithm | Supported | @@ -137,11 +140,17 @@ If a useful algorithm is not included in the above table, it SHOULD be submitted [SHA-256][rfc4634-s4.1] is a collision-resistant hash function, chosen for ubiquity, reasonable size and secure characteristics. Implementations MUST implement SHA-256 digest verification for use in descriptors. +When the _algorithm identifier_ is `sha256`, the _encoded_ portion MUST match `/[a-f0-9]{64}/`. +Note that `[A-F]` MUST NOT be used here. + #### SHA-512 [SHA-512][rfc4634-s4.2] is a collision-resistant hash function which [may be more perfomant][sha256-vs-sha512] than [SHA-256](#sha-256) on some CPUs. Implementations MAY implement SHA-512 digest verification for use in descriptors. +When the _algorithm identifier_ is `sha512`, the _encoded_ portion MUST match `/[a-f0-9]{128}/`. +Note that `[A-F]` MUST NOT be used here. + ## Examples The following example describes a [_Manifest_](manifest.md#image-manifest) with a content identifier of "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270" and a size of 7682 bytes: diff --git a/schema/descriptor_test.go b/schema/descriptor_test.go index 009614330..ecdbe52b1 100644 --- a/schema/descriptor_test.go +++ b/schema/descriptor_test.go @@ -191,7 +191,7 @@ func TestDescriptor(t *testing.T) { fail: true, }, - // expected failure: digest does not match pattern (invalid hash characters) + // expected failure: digest does not match pattern (characters needs to be lower for sha256) { descriptor: ` { @@ -200,6 +200,7 @@ func TestDescriptor(t *testing.T) { "digest": "sha256:5B0BCABD1ED22E9FB1310CF6C2DEC7CDEF19F0AD69EFA1F392E94A4333501270" } `, + fail: true, }, // expected success: valid URL entry diff --git a/schema/validator.go b/schema/validator.go index 1006b510f..8a4ef75fa 100644 --- a/schema/validator.go +++ b/schema/validator.go @@ -20,7 +20,9 @@ import ( "fmt" "io" "io/ioutil" + "regexp" + digest "github.com/opencontainers/go-digest" "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/xeipuuv/gojsonschema" @@ -33,7 +35,8 @@ type Validator string type validateDescendantsFunc func(r io.Reader) error var mapValidateDescendants = map[Validator]validateDescendantsFunc{ - ValidatorMediaTypeManifest: validateManifestDescendants, + ValidatorMediaTypeManifest: validateManifestDescendants, + ValidatorMediaTypeDescriptor: validateDescriptorDescendants, } // ValidationError contains all the errors that happened during validation. @@ -119,3 +122,39 @@ func validateManifestDescendants(r io.Reader) error { } return nil } + +var ( + sha256EncodedRegexp = regexp.MustCompile(`^[a-f0-9]{64}$`) + sha512EncodedRegexp = regexp.MustCompile(`^[a-f0-9]{128}$`) +) + +func validateDescriptorDescendants(r io.Reader) error { + header := v1.Descriptor{} + + buf, err := ioutil.ReadAll(r) + if err != nil { + return errors.Wrapf(err, "error reading the io stream") + } + + err = json.Unmarshal(buf, &header) + if err != nil { + return errors.Wrap(err, "descriptor format mismatch") + } + + if header.Digest.Validate() != nil { + // we ignore unsupported algorithms + fmt.Printf("warning: unsupported digest: %q: %v\n", header.Digest, err) + return nil + } + switch header.Digest.Algorithm() { + case digest.SHA256: + if !sha256EncodedRegexp.MatchString(header.Digest.Hex()) { + return errors.Errorf("unexpected sha256 digest: %q", header.Digest) + } + case digest.SHA512: + if !sha512EncodedRegexp.MatchString(header.Digest.Hex()) { + return errors.Errorf("unexpected sha512 digest: %q", header.Digest) + } + } + return nil +}