Skip to content

Commit 3437101

Browse files
authored
Merge pull request opencontainers#667 from AkihiroSuda/sha256-chars
descriptor: sha{256,512}: disallow /[A-F]/ characters
2 parents b25e32a + 5302569 commit 3437101

File tree

3 files changed

+51
-2
lines changed

3 files changed

+51
-2
lines changed

descriptor.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ algorithm-separator := /[+._-]/
7676
encoded := /[a-zA-Z0-9=_-]+/
7777
```
7878

79+
Note that _algorithm_ MAY impose algorithm-specific restriction on the grammar of the _encoded_ portion.
80+
See also [Registered Algorithms](#registered-identifiers).
81+
7982
Some example digest strings include the following:
8083

8184
digest | algorithm | Registered |
@@ -137,11 +140,17 @@ If a useful algorithm is not included in the above table, it SHOULD be submitted
137140
[SHA-256][rfc4634-s4.1] is a collision-resistant hash function, chosen for ubiquity, reasonable size and secure characteristics.
138141
Implementations MUST implement SHA-256 digest verification for use in descriptors.
139142

143+
When the _algorithm identifier_ is `sha256`, the _encoded_ portion MUST match `/[a-f0-9]{64}/`.
144+
Note that `[A-F]` MUST NOT be used here.
145+
140146
#### SHA-512
141147

142148
[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.
143149
Implementations MAY implement SHA-512 digest verification for use in descriptors.
144150

151+
When the _algorithm identifier_ is `sha512`, the _encoded_ portion MUST match `/[a-f0-9]{128}/`.
152+
Note that `[A-F]` MUST NOT be used here.
153+
145154
## Examples
146155

147156
The following example describes a [_Manifest_](manifest.md#image-manifest) with a content identifier of "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270" and a size of 7682 bytes:

schema/descriptor_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ func TestDescriptor(t *testing.T) {
191191
fail: true,
192192
},
193193

194-
// expected failure: digest does not match pattern (invalid hash characters)
194+
// expected failure: digest does not match pattern (characters needs to be lower for sha256)
195195
{
196196
descriptor: `
197197
{
@@ -200,6 +200,7 @@ func TestDescriptor(t *testing.T) {
200200
"digest": "sha256:5B0BCABD1ED22E9FB1310CF6C2DEC7CDEF19F0AD69EFA1F392E94A4333501270"
201201
}
202202
`,
203+
fail: true,
203204
},
204205

205206
// expected success: valid URL entry

schema/validator.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ import (
2020
"fmt"
2121
"io"
2222
"io/ioutil"
23+
"regexp"
2324

25+
digest "github.com/opencontainers/go-digest"
2426
"github.com/opencontainers/image-spec/specs-go/v1"
2527
"github.com/pkg/errors"
2628
"github.com/xeipuuv/gojsonschema"
@@ -33,7 +35,8 @@ type Validator string
3335
type validateDescendantsFunc func(r io.Reader) error
3436

3537
var mapValidateDescendants = map[Validator]validateDescendantsFunc{
36-
ValidatorMediaTypeManifest: validateManifestDescendants,
38+
ValidatorMediaTypeManifest: validateManifestDescendants,
39+
ValidatorMediaTypeDescriptor: validateDescriptorDescendants,
3740
}
3841

3942
// ValidationError contains all the errors that happened during validation.
@@ -119,3 +122,39 @@ func validateManifestDescendants(r io.Reader) error {
119122
}
120123
return nil
121124
}
125+
126+
var (
127+
sha256EncodedRegexp = regexp.MustCompile(`^[a-f0-9]{64}$`)
128+
sha512EncodedRegexp = regexp.MustCompile(`^[a-f0-9]{128}$`)
129+
)
130+
131+
func validateDescriptorDescendants(r io.Reader) error {
132+
header := v1.Descriptor{}
133+
134+
buf, err := ioutil.ReadAll(r)
135+
if err != nil {
136+
return errors.Wrapf(err, "error reading the io stream")
137+
}
138+
139+
err = json.Unmarshal(buf, &header)
140+
if err != nil {
141+
return errors.Wrap(err, "descriptor format mismatch")
142+
}
143+
144+
if header.Digest.Validate() != nil {
145+
// we ignore unsupported algorithms
146+
fmt.Printf("warning: unsupported digest: %q: %v\n", header.Digest, err)
147+
return nil
148+
}
149+
switch header.Digest.Algorithm() {
150+
case digest.SHA256:
151+
if !sha256EncodedRegexp.MatchString(header.Digest.Hex()) {
152+
return errors.Errorf("unexpected sha256 digest: %q", header.Digest)
153+
}
154+
case digest.SHA512:
155+
if !sha512EncodedRegexp.MatchString(header.Digest.Hex()) {
156+
return errors.Errorf("unexpected sha512 digest: %q", header.Digest)
157+
}
158+
}
159+
return nil
160+
}

0 commit comments

Comments
 (0)