Decouple manifest signing and verification
It was probably ill-advised to couple manifest signing and verification to their respective types. This changeset simply changes them from methods to functions. These might not even be in this package in the future. Signed-off-by: Stephen J Day <stephen.day@docker.com>
This commit is contained in:
parent
579aa3b617
commit
f1f610c6cd
7 changed files with 108 additions and 95 deletions
|
@ -345,7 +345,7 @@ func TestManifestAPI(t *testing.T) {
|
||||||
|
|
||||||
// -------------------
|
// -------------------
|
||||||
// Push the signed manifest with all layers pushed.
|
// Push the signed manifest with all layers pushed.
|
||||||
signedManifest, err := unsignedManifest.Sign(pk)
|
signedManifest, err := manifest.Sign(unsignedManifest, pk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error signing manifest: %v", err)
|
t.Fatalf("unexpected error signing manifest: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
package manifest
|
package manifest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/x509"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
"github.com/docker/distribution/digest"
|
"github.com/docker/distribution/digest"
|
||||||
"github.com/docker/libtrust"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Versioned provides a struct with just the manifest schemaVersion. Incoming
|
// Versioned provides a struct with just the manifest schemaVersion. Incoming
|
||||||
|
@ -39,64 +36,6 @@ type Manifest struct {
|
||||||
History []ManifestHistory `json:"history"`
|
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
|
// SignedManifest provides an envelope for a signed image manifest, including
|
||||||
// the format sensitive raw bytes. It contains fields to
|
// the format sensitive raw bytes. It contains fields to
|
||||||
type SignedManifest struct {
|
type SignedManifest struct {
|
||||||
|
@ -109,30 +48,6 @@ type SignedManifest struct {
|
||||||
Raw []byte `json:"-"`
|
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.
|
// UnmarshalJSON populates a new ImageManifest struct from JSON data.
|
||||||
func (sm *SignedManifest) UnmarshalJSON(b []byte) error {
|
func (sm *SignedManifest) UnmarshalJSON(b []byte) error {
|
||||||
var manifest Manifest
|
var manifest Manifest
|
||||||
|
|
|
@ -47,7 +47,7 @@ func TestManifestUnmarshaling(t *testing.T) {
|
||||||
func TestManifestVerification(t *testing.T) {
|
func TestManifestVerification(t *testing.T) {
|
||||||
env := genEnv(t)
|
env := genEnv(t)
|
||||||
|
|
||||||
publicKeys, err := env.signed.Verify()
|
publicKeys, err := Verify(env.signed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error verifying manifest: %v", err)
|
t.Fatalf("error verifying manifest: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ func genEnv(t *testing.T) *testEnv {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
sm, err := m.Sign(pk)
|
sm, err := Sign(&m, pk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error signing manifest: %v", err)
|
t.Fatalf("error signing manifest: %v", err)
|
||||||
}
|
}
|
||||||
|
|
66
manifest/sign.go
Normal file
66
manifest/sign.go
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package manifest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/docker/libtrust"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 Sign(m *Manifest, 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 SignWithChain(m *Manifest, 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
|
||||||
|
}
|
32
manifest/verify.go
Normal file
32
manifest/verify.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package manifest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/libtrust"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Verify verifies the signature of the signed manifest returning the public
|
||||||
|
// keys used during signing.
|
||||||
|
func Verify(sm *SignedManifest) ([]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 VerifyChains(sm *SignedManifest, ca *x509.CertPool) ([][]*x509.Certificate, error) {
|
||||||
|
js, err := libtrust.ParsePrettySignature(sm.Raw, "signatures")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return js.VerifyChains(ca)
|
||||||
|
}
|
|
@ -186,19 +186,19 @@ func (ms *manifestStore) path(name, tag string) (string, error) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ms *manifestStore) verifyManifest(name, tag string, manifest *manifest.SignedManifest) error {
|
func (ms *manifestStore) verifyManifest(name, tag string, mnfst *manifest.SignedManifest) error {
|
||||||
// TODO(stevvooe): This verification is present here, but this needs to be
|
// TODO(stevvooe): This verification is present here, but this needs to be
|
||||||
// lifted out of the storage infrastructure and moved into a package
|
// lifted out of the storage infrastructure and moved into a package
|
||||||
// oriented towards defining verifiers and reporting them with
|
// oriented towards defining verifiers and reporting them with
|
||||||
// granularity.
|
// granularity.
|
||||||
|
|
||||||
var errs ErrManifestVerification
|
var errs ErrManifestVerification
|
||||||
if manifest.Name != name {
|
if mnfst.Name != name {
|
||||||
// TODO(stevvooe): This needs to be an exported error
|
// TODO(stevvooe): This needs to be an exported error
|
||||||
errs = append(errs, fmt.Errorf("name does not match manifest name"))
|
errs = append(errs, fmt.Errorf("name does not match manifest name"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if manifest.Tag != tag {
|
if mnfst.Tag != tag {
|
||||||
// TODO(stevvooe): This needs to be an exported error.
|
// TODO(stevvooe): This needs to be an exported error.
|
||||||
errs = append(errs, fmt.Errorf("tag does not match manifest tag"))
|
errs = append(errs, fmt.Errorf("tag does not match manifest tag"))
|
||||||
}
|
}
|
||||||
|
@ -207,7 +207,7 @@ func (ms *manifestStore) verifyManifest(name, tag string, manifest *manifest.Sig
|
||||||
// VerifyWithChains. We need to define the exact source of the CA.
|
// VerifyWithChains. We need to define the exact source of the CA.
|
||||||
// Perhaps, its a configuration value injected into manifest store.
|
// Perhaps, its a configuration value injected into manifest store.
|
||||||
|
|
||||||
if _, err := manifest.Verify(); err != nil {
|
if _, err := manifest.Verify(mnfst); err != nil {
|
||||||
switch err {
|
switch err {
|
||||||
case libtrust.ErrMissingSignatureKey, libtrust.ErrInvalidJSONContent, libtrust.ErrMissingSignatureKey:
|
case libtrust.ErrMissingSignatureKey, libtrust.ErrInvalidJSONContent, libtrust.ErrMissingSignatureKey:
|
||||||
errs = append(errs, ErrManifestUnverified{})
|
errs = append(errs, ErrManifestUnverified{})
|
||||||
|
@ -220,7 +220,7 @@ func (ms *manifestStore) verifyManifest(name, tag string, manifest *manifest.Sig
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, fsLayer := range manifest.FSLayers {
|
for _, fsLayer := range mnfst.FSLayers {
|
||||||
exists, err := ms.layerService.Exists(name, fsLayer.BlobSum)
|
exists, err := ms.layerService.Exists(name, fsLayer.BlobSum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
|
|
|
@ -42,7 +42,7 @@ func TestManifestStorage(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
manifest := manifest.Manifest{
|
m := manifest.Manifest{
|
||||||
Versioned: manifest.Versioned{
|
Versioned: manifest.Versioned{
|
||||||
SchemaVersion: 1,
|
SchemaVersion: 1,
|
||||||
},
|
},
|
||||||
|
@ -63,7 +63,7 @@ func TestManifestStorage(t *testing.T) {
|
||||||
t.Fatalf("unexpected error generating private key: %v", err)
|
t.Fatalf("unexpected error generating private key: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sm, err := manifest.Sign(pk)
|
sm, err := manifest.Sign(&m, pk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error signing manifest: %v", err)
|
t.Fatalf("error signing manifest: %v", err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue