From f1f610c6cd6124a58441b3e659e601a468855f65 Mon Sep 17 00:00:00 2001 From: Stephen J Day Date: Fri, 2 Jan 2015 15:46:47 -0800 Subject: [PATCH] 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 --- api_test.go | 2 +- manifest/manifest.go | 85 ----------------------------------- manifest/manifest_test.go | 4 +- manifest/sign.go | 66 +++++++++++++++++++++++++++ manifest/verify.go | 32 +++++++++++++ storage/manifeststore.go | 10 ++--- storage/manifeststore_test.go | 4 +- 7 files changed, 108 insertions(+), 95 deletions(-) create mode 100644 manifest/sign.go create mode 100644 manifest/verify.go diff --git a/api_test.go b/api_test.go index d3085ade..6e8c403c 100644 --- a/api_test.go +++ b/api_test.go @@ -345,7 +345,7 @@ func TestManifestAPI(t *testing.T) { // ------------------- // Push the signed manifest with all layers pushed. - signedManifest, err := unsignedManifest.Sign(pk) + signedManifest, err := manifest.Sign(unsignedManifest, pk) if err != nil { t.Fatalf("unexpected error signing manifest: %v", err) } diff --git a/manifest/manifest.go b/manifest/manifest.go index 75816a80..3fc1dc45 100644 --- a/manifest/manifest.go +++ b/manifest/manifest.go @@ -1,12 +1,9 @@ package manifest import ( - "crypto/x509" "encoding/json" - "github.com/Sirupsen/logrus" "github.com/docker/distribution/digest" - "github.com/docker/libtrust" ) // Versioned provides a struct with just the manifest schemaVersion. Incoming @@ -39,64 +36,6 @@ type Manifest struct { 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 // the format sensitive raw bytes. It contains fields to type SignedManifest struct { @@ -109,30 +48,6 @@ type SignedManifest struct { 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. func (sm *SignedManifest) UnmarshalJSON(b []byte) error { var manifest Manifest diff --git a/manifest/manifest_test.go b/manifest/manifest_test.go index 5d564ec8..941bfde9 100644 --- a/manifest/manifest_test.go +++ b/manifest/manifest_test.go @@ -47,7 +47,7 @@ func TestManifestUnmarshaling(t *testing.T) { func TestManifestVerification(t *testing.T) { env := genEnv(t) - publicKeys, err := env.signed.Verify() + publicKeys, err := Verify(env.signed) if err != nil { 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 { t.Fatalf("error signing manifest: %v", err) } diff --git a/manifest/sign.go b/manifest/sign.go new file mode 100644 index 00000000..a4c37652 --- /dev/null +++ b/manifest/sign.go @@ -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 +} diff --git a/manifest/verify.go b/manifest/verify.go new file mode 100644 index 00000000..3e051b26 --- /dev/null +++ b/manifest/verify.go @@ -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) +} diff --git a/storage/manifeststore.go b/storage/manifeststore.go index 8e5be6a7..af16dcf3 100644 --- a/storage/manifeststore.go +++ b/storage/manifeststore.go @@ -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 // lifted out of the storage infrastructure and moved into a package // oriented towards defining verifiers and reporting them with // granularity. var errs ErrManifestVerification - if manifest.Name != name { + if mnfst.Name != name { // TODO(stevvooe): This needs to be an exported error 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. 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. // Perhaps, its a configuration value injected into manifest store. - if _, err := manifest.Verify(); err != nil { + if _, err := manifest.Verify(mnfst); err != nil { switch err { case libtrust.ErrMissingSignatureKey, libtrust.ErrInvalidJSONContent, libtrust.ErrMissingSignatureKey: 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) if err != nil { errs = append(errs, err) diff --git a/storage/manifeststore_test.go b/storage/manifeststore_test.go index 313c30cf..a6a00aa1 100644 --- a/storage/manifeststore_test.go +++ b/storage/manifeststore_test.go @@ -42,7 +42,7 @@ func TestManifestStorage(t *testing.T) { } } - manifest := manifest.Manifest{ + m := manifest.Manifest{ Versioned: manifest.Versioned{ SchemaVersion: 1, }, @@ -63,7 +63,7 @@ func TestManifestStorage(t *testing.T) { t.Fatalf("unexpected error generating private key: %v", err) } - sm, err := manifest.Sign(pk) + sm, err := manifest.Sign(&m, pk) if err != nil { t.Fatalf("error signing manifest: %v", err) }