diff --git a/api_test.go b/api_test.go index d3085adea..6e8c403c7 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 75816a806..3fc1dc45e 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 5d564ec8b..941bfde9a 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 000000000..a4c37652e --- /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 000000000..3e051b266 --- /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 8e5be6a7d..af16dcf33 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 313c30cf5..a6a00aa1b 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) }