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:
Stephen J Day 2015-01-02 15:46:47 -08:00
parent 579aa3b617
commit f1f610c6cd
7 changed files with 108 additions and 95 deletions

View file

@ -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)
} }

View file

@ -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

View file

@ -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
View 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
View 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)
}

View file

@ -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)

View file

@ -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)
} }