1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package signjar
18
19 import (
20 "crypto"
21 "encoding/base64"
22 "errors"
23 "fmt"
24 "io"
25 "io/ioutil"
26 "net/http"
27 "strings"
28
29 "github.com/sassoftware/relic/lib/x509tools"
30 "github.com/sassoftware/relic/lib/zipslicer"
31 )
32
33
34
35 var jarMagic = []byte{0xfe, 0xca, 0, 0}
36
37 type JarDigest struct {
38 Digests map[string]string
39 Manifest []byte
40 Hash crypto.Hash
41 inz *zipslicer.Directory
42 }
43
44 func DigestJarStream(r io.Reader, hash crypto.Hash) (*JarDigest, error) {
45 inz, err := zipslicer.ReadZipTar(r)
46 if err != nil {
47 return nil, err
48 }
49 return updateManifest(inz, hash)
50 }
51
52
53 func digestFiles(jar *zipslicer.Directory, hash crypto.Hash) (*JarDigest, error) {
54 jd := &JarDigest{
55 Hash: hash,
56 Digests: make(map[string]string),
57 inz: jar,
58 }
59 for _, f := range jar.File {
60 if f.Name == manifestName {
61 r, err := f.Open()
62 if err != nil {
63 return nil, fmt.Errorf("failed to read JAR manifest: %s", err)
64 }
65 jd.Manifest, err = ioutil.ReadAll(r)
66 if err != nil {
67 return nil, fmt.Errorf("failed to read JAR manifest: %s", err)
68 }
69 } else if (len(f.Name) > 0 && f.Name[len(f.Name)-1] == '/') || strings.HasPrefix(f.Name, "META-INF/") {
70
71 } else {
72 r, err := f.Open()
73 if err != nil {
74 return nil, err
75 }
76 d := hash.New()
77 if _, err := io.Copy(d, r); err != nil {
78 return nil, fmt.Errorf("failed to digest JAR file %s: %s", f.Name, err)
79 }
80 if err := r.Close(); err != nil {
81 return nil, fmt.Errorf("failed to digest JAR file %s: %s", f.Name, err)
82 }
83 jd.Digests[f.Name] = base64.StdEncoding.EncodeToString(d.Sum(nil))
84 }
85
86
87
88 if _, err := f.GetDataDescriptor(); err != nil {
89 return nil, fmt.Errorf("failed to read JAR manifest: %s", err)
90 }
91 }
92 return jd, nil
93 }
94
95
96 func updateManifest(jar *zipslicer.Directory, hash crypto.Hash) (*JarDigest, error) {
97 jd, err := digestFiles(jar, hash)
98 if err != nil {
99 return nil, err
100 } else if jd.Manifest == nil {
101 return nil, errors.New("JAR did not contain a manifest")
102 }
103 files, err := ParseManifest(jd.Manifest)
104 if err != nil {
105 return nil, err
106 }
107
108 hashName := x509tools.HashNames[hash]
109 if hashName == "" {
110 return nil, errors.New("unsupported hash type")
111 }
112 hashName += "-Digest"
113 changed := false
114 for name, calculated := range jd.Digests {
115
116 attrs := files.Files[name]
117 if attrs == nil {
118
119 files.Files[name] = http.Header{
120 "Name": []string{name},
121 hashName: []string{calculated},
122 }
123 files.Order = append(files.Order, name)
124 changed = true
125 } else if attrs.Get("Magic") != "" {
126
127 } else if existing := attrs.Get(hashName); existing != "" {
128
129 if existing != calculated {
130 return nil, fmt.Errorf("%s mismatch for JAR file %s: manifest %s != calculated %s", hashName, name, existing, calculated)
131 }
132 } else {
133
134 attrs.Set(hashName, calculated)
135 changed = true
136 }
137 }
138 if changed {
139 jd.Manifest = files.Dump()
140 }
141 return jd, nil
142 }
143
View as plain text