github.com/ld86/docker@v1.7.1-rc3/graph/manifest.go (about) 1 package graph 2 3 import ( 4 "encoding/json" 5 "fmt" 6 7 "github.com/Sirupsen/logrus" 8 "github.com/docker/distribution/digest" 9 "github.com/docker/docker/registry" 10 "github.com/docker/docker/trust" 11 "github.com/docker/libtrust" 12 ) 13 14 // loadManifest loads a manifest from a byte array and verifies its content, 15 // returning the local digest, the manifest itself, whether or not it was 16 // verified. If ref is a digest, rather than a tag, this will be treated as 17 // the local digest. An error will be returned if the signature verification 18 // fails, local digest verification fails and, if provided, the remote digest 19 // verification fails. The boolean return will only be false without error on 20 // the failure of signatures trust check. 21 func (s *TagStore) loadManifest(manifestBytes []byte, ref string, remoteDigest digest.Digest) (digest.Digest, *registry.ManifestData, bool, error) { 22 payload, keys, err := unpackSignedManifest(manifestBytes) 23 if err != nil { 24 return "", nil, false, fmt.Errorf("error unpacking manifest: %v", err) 25 } 26 27 // TODO(stevvooe): It would be a lot better here to build up a stack of 28 // verifiers, then push the bytes one time for signatures and digests, but 29 // the manifests are typically small, so this optimization is not worth 30 // hacking this code without further refactoring. 31 32 var localDigest digest.Digest 33 34 // Verify the local digest, if present in ref. ParseDigest will validate 35 // that the ref is a digest and verify against that if present. Otherwize 36 // (on error), we simply compute the localDigest and proceed. 37 if dgst, err := digest.ParseDigest(ref); err == nil { 38 // verify the manifest against local ref 39 if err := verifyDigest(dgst, payload); err != nil { 40 return "", nil, false, fmt.Errorf("verifying local digest: %v", err) 41 } 42 43 localDigest = dgst 44 } else { 45 // We don't have a local digest, since we are working from a tag. 46 // Compute the digest of the payload and return that. 47 logrus.Debugf("provided manifest reference %q is not a digest: %v", ref, err) 48 localDigest, err = digest.FromBytes(payload) 49 if err != nil { 50 // near impossible 51 logrus.Errorf("error calculating local digest during tag pull: %v", err) 52 return "", nil, false, err 53 } 54 } 55 56 // verify against the remote digest, if available 57 if remoteDigest != "" { 58 if err := verifyDigest(remoteDigest, payload); err != nil { 59 return "", nil, false, fmt.Errorf("verifying remote digest: %v", err) 60 } 61 } 62 63 var manifest registry.ManifestData 64 if err := json.Unmarshal(payload, &manifest); err != nil { 65 return "", nil, false, fmt.Errorf("error unmarshalling manifest: %s", err) 66 } 67 68 // validate the contents of the manifest 69 if err := validateManifest(&manifest); err != nil { 70 return "", nil, false, err 71 } 72 73 var verified bool 74 verified, err = s.verifyTrustedKeys(manifest.Name, keys) 75 if err != nil { 76 return "", nil, false, fmt.Errorf("error verifying trusted keys: %v", err) 77 } 78 79 return localDigest, &manifest, verified, nil 80 } 81 82 // unpackSignedManifest takes the raw, signed manifest bytes, unpacks the jws 83 // and returns the payload and public keys used to signed the manifest. 84 // Signatures are verified for authenticity but not against the trust store. 85 func unpackSignedManifest(p []byte) ([]byte, []libtrust.PublicKey, error) { 86 sig, err := libtrust.ParsePrettySignature(p, "signatures") 87 if err != nil { 88 return nil, nil, fmt.Errorf("error parsing payload: %s", err) 89 } 90 91 keys, err := sig.Verify() 92 if err != nil { 93 return nil, nil, fmt.Errorf("error verifying payload: %s", err) 94 } 95 96 payload, err := sig.Payload() 97 if err != nil { 98 return nil, nil, fmt.Errorf("error retrieving payload: %s", err) 99 } 100 101 return payload, keys, nil 102 } 103 104 // verifyTrustedKeys checks the keys provided against the trust store, 105 // ensuring that the provided keys are trusted for the namespace. The keys 106 // provided from this method must come from the signatures provided as part of 107 // the manifest JWS package, obtained from unpackSignedManifest or libtrust. 108 func (s *TagStore) verifyTrustedKeys(namespace string, keys []libtrust.PublicKey) (verified bool, err error) { 109 if namespace[0] != '/' { 110 namespace = "/" + namespace 111 } 112 113 for _, key := range keys { 114 b, err := key.MarshalJSON() 115 if err != nil { 116 return false, fmt.Errorf("error marshalling public key: %s", err) 117 } 118 // Check key has read/write permission (0x03) 119 v, err := s.trustService.CheckKey(namespace, b, 0x03) 120 if err != nil { 121 vErr, ok := err.(trust.NotVerifiedError) 122 if !ok { 123 return false, fmt.Errorf("error running key check: %s", err) 124 } 125 logrus.Debugf("Key check result: %v", vErr) 126 } 127 verified = v 128 } 129 130 if verified { 131 logrus.Debug("Key check result: verified") 132 } 133 134 return 135 } 136 137 // verifyDigest checks the contents of p against the provided digest. Note 138 // that for manifests, this is the signed payload and not the raw bytes with 139 // signatures. 140 func verifyDigest(dgst digest.Digest, p []byte) error { 141 if err := dgst.Validate(); err != nil { 142 return fmt.Errorf("error validating digest %q: %v", dgst, err) 143 } 144 145 verifier, err := digest.NewDigestVerifier(dgst) 146 if err != nil { 147 // There are not many ways this can go wrong: if it does, its 148 // fatal. Likley, the cause would be poor validation of the 149 // incoming reference. 150 return fmt.Errorf("error creating verifier for digest %q: %v", dgst, err) 151 } 152 153 if _, err := verifier.Write(p); err != nil { 154 return fmt.Errorf("error writing payload to digest verifier (verifier target %q): %v", dgst, err) 155 } 156 157 if !verifier.Verified() { 158 return fmt.Errorf("verification against digest %q failed", dgst) 159 } 160 161 return nil 162 } 163 164 func validateManifest(manifest *registry.ManifestData) error { 165 if manifest.SchemaVersion != 1 { 166 return fmt.Errorf("unsupported schema version: %d", manifest.SchemaVersion) 167 } 168 169 if len(manifest.FSLayers) != len(manifest.History) { 170 return fmt.Errorf("length of history not equal to number of layers") 171 } 172 173 if len(manifest.FSLayers) == 0 { 174 return fmt.Errorf("no FSLayers in manifest") 175 } 176 177 return nil 178 }