Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(402)

Side by Side Diff: third_party/go/src/golang.org/x/mobile/cmd/gomobile/writer.go

Issue 1275153002: Remove third_party/golang.org/x/mobile as it is no longer used with Go 1.5. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Remove golang.org/x/mobile Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package main
6
7 // APK is the archival format used for Android apps. It is a ZIP archive with
8 // three extra files:
9 //
10 // META-INF/MANIFEST.MF
11 // META-INF/CERT.SF
12 // META-INF/CERT.RSA
13 //
14 // The MANIFEST.MF comes from the Java JAR archive format. It is a list of
15 // files included in the archive along with a SHA1 hash, for example:
16 //
17 // Name: lib/armeabi/libbasic.so
18 // SHA1-Digest: ntLSc1eLCS2Tq1oB4Vw6jvkranw=
19 //
20 // For debugging, the equivalent SHA1-Digest can be generated with OpenSSL:
21 //
22 // cat lib/armeabi/libbasic.so | openssl sha1 -binary | openssl base64
23 //
24 // CERT.SF is a similar manifest. It begins with a SHA1 digest of the entire
25 // manifest file:
26 //
27 // Signature-Version: 1.0
28 // Created-By: 1.0 (Android)
29 // SHA1-Digest-Manifest: aJw+u+10C3Enbg8XRCN6jepluYA=
30 //
31 // Then for each entry in the manifest it has a SHA1 digest of the manfiest's
32 // hash combined with the file name:
33 //
34 // Name: lib/armeabi/libbasic.so
35 // SHA1-Digest: Q7NAS6uzrJr6WjePXSGT+vvmdiw=
36 //
37 // This can also be generated with openssl:
38 //
39 // echo -en "Name: lib/armeabi/libbasic.so\r\nSHA1-Digest: ntLSc1eLCS2Tq1oB 4Vw6jvkranw=\r\n\r\n" | openssl sha1 -binary | openssl base64
40 //
41 // Note the \r\n line breaks.
42 //
43 // CERT.RSA is an RSA signature block made of CERT.SF. Verify it with:
44 //
45 // openssl smime -verify -in CERT.RSA -inform DER -content CERT.SF cert.pem
46 //
47 // The APK format imposes two extra restrictions on the ZIP format. First,
48 // it is uncompressed. Second, each contained file is 4-byte aligned. This
49 // allows the Android OS to mmap contents without unpacking the archive.
50
51 // Note: to make life a little harder, Android Studio stores the RSA key used
52 // for signing in an Oracle Java proprietary keystore format, JKS. For example,
53 // the generated debug key is in ~/.android/debug.keystore, and can be
54 // extracted using the JDK's keytool utility:
55 //
56 // keytool -importkeystore -srckeystore ~/.android/debug.keystore -destkeys tore ~/.android/debug.p12 -deststoretype PKCS12
57 //
58 // Once in standard PKCS12, the key can be converted to PEM for use in the
59 // Go crypto packages:
60 //
61 // openssl pkcs12 -in ~/.android/debug.p12 -nocerts -nodes -out ~/.android/ debug.pem
62 //
63 // Fortunately for debug builds, all that matters is that the APK is signed.
64 // The choice of key is unimportant, so we can generate one for normal builds.
65 // For production builds, we can ask users to provide a PEM file.
66
67 import (
68 "archive/zip"
69 "bytes"
70 "crypto/rand"
71 "crypto/rsa"
72 "crypto/sha1"
73 "encoding/base64"
74 "fmt"
75 "hash"
76 "io"
77 )
78
79 // NewWriter returns a new Writer writing an APK file to w.
80 // The APK will be signed with key.
81 func NewWriter(w io.Writer, priv *rsa.PrivateKey) *Writer {
82 apkw := &Writer{priv: priv}
83 apkw.w = zip.NewWriter(&countWriter{apkw: apkw, w: w})
84 return apkw
85 }
86
87 // Writer implements an APK file writer.
88 type Writer struct {
89 offset int
90 w *zip.Writer
91 priv *rsa.PrivateKey
92 manifest []manifestEntry
93 cur *fileWriter
94 }
95
96 // Create adds a file to the APK archive using the provided name.
97 //
98 // The name must be a relative path. The file's contents must be written to
99 // the returned io.Writer before the next call to Create or Close.
100 func (w *Writer) Create(name string) (io.Writer, error) {
101 if err := w.clearCur(); err != nil {
102 return nil, fmt.Errorf("apk: Create(%s): %v", name, err)
103 }
104 if name == "AndroidManifest.xml" {
105 w.cur = &fileWriter{
106 name: name,
107 w: new(bytes.Buffer),
108 sha1: sha1.New(),
109 }
110 return w.cur, nil
111 }
112 res, err := w.create(name)
113 if err != nil {
114 return nil, fmt.Errorf("apk: Create(%s): %v", name, err)
115 }
116 return res, nil
117 }
118
119 func (w *Writer) create(name string) (io.Writer, error) {
120 // Align start of file contents by using Extra as padding.
121 if err := w.w.Flush(); err != nil { // for exact offset
122 return nil, err
123 }
124 const fileHeaderLen = 30 // + filename + extra
125 start := w.offset + fileHeaderLen + len(name)
126 extra := start % 4
127
128 zipfw, err := w.w.CreateHeader(&zip.FileHeader{
129 Name: name,
130 Extra: make([]byte, extra),
131 })
132 if err != nil {
133 return nil, err
134 }
135 w.cur = &fileWriter{
136 name: name,
137 w: zipfw,
138 sha1: sha1.New(),
139 }
140 return w.cur, nil
141 }
142
143 // Close finishes writing the APK. This includes writing the manifest and
144 // signing the archive, and writing the ZIP central directory.
145 //
146 // It does not close the underlying writer.
147 func (w *Writer) Close() error {
148 if err := w.clearCur(); err != nil {
149 return fmt.Errorf("apk: %v", err)
150 }
151
152 manifest := new(bytes.Buffer)
153 fmt.Fprint(manifest, manifestHeader)
154 certBody := new(bytes.Buffer)
155
156 for _, entry := range w.manifest {
157 n := entry.name
158 h := base64.StdEncoding.EncodeToString(entry.sha1.Sum(nil))
159 fmt.Fprintf(manifest, "Name: %s\nSHA1-Digest: %s\n\n", n, h)
160 cHash := sha1.New()
161 fmt.Fprintf(cHash, "Name: %s\r\nSHA1-Digest: %s\r\n\r\n", n, h)
162 ch := base64.StdEncoding.EncodeToString(cHash.Sum(nil))
163 fmt.Fprintf(certBody, "Name: %s\nSHA1-Digest: %s\n\n", n, ch)
164 }
165
166 mHash := sha1.New()
167 mHash.Write(manifest.Bytes())
168 cert := new(bytes.Buffer)
169 fmt.Fprint(cert, certHeader)
170 fmt.Fprintf(cert, "SHA1-Digest-Manifest: %s\n\n", base64.StdEncoding.Enc odeToString(mHash.Sum(nil)))
171 cert.Write(certBody.Bytes())
172
173 mw, err := w.Create("META-INF/MANIFEST.MF")
174 if err != nil {
175 return err
176 }
177 if _, err := mw.Write(manifest.Bytes()); err != nil {
178 return err
179 }
180
181 cw, err := w.Create("META-INF/CERT.SF")
182 if err != nil {
183 return err
184 }
185 if _, err := cw.Write(cert.Bytes()); err != nil {
186 return err
187 }
188
189 rsa, err := signPKCS7(rand.Reader, w.priv, cert.Bytes())
190 if err != nil {
191 return fmt.Errorf("apk: %v", err)
192 }
193 rw, err := w.Create("META-INF/CERT.RSA")
194 if err != nil {
195 return err
196 }
197 if _, err := rw.Write(rsa); err != nil {
198 return err
199 }
200
201 return w.w.Close()
202 }
203
204 const manifestHeader = `Manifest-Version: 1.0
205 Created-By: 1.0 (Go)
206
207 `
208
209 const certHeader = `Signature-Version: 1.0
210 Created-By: 1.0 (Go)
211 `
212
213 func (w *Writer) clearCur() error {
214 if w.cur == nil {
215 return nil
216 }
217 if w.cur.name == "AndroidManifest.xml" {
218 buf := w.cur.w.(*bytes.Buffer)
219 b, err := binaryXML(buf)
220 if err != nil {
221 return err
222 }
223 f, err := w.create("AndroidManifest.xml")
224 if err != nil {
225 return err
226 }
227 if _, err := f.Write(b); err != nil {
228 return err
229 }
230 }
231 w.manifest = append(w.manifest, manifestEntry{
232 name: w.cur.name,
233 sha1: w.cur.sha1,
234 })
235 w.cur.closed = true
236 w.cur = nil
237 return nil
238 }
239
240 type manifestEntry struct {
241 name string
242 sha1 hash.Hash
243 }
244
245 type countWriter struct {
246 apkw *Writer
247 w io.Writer
248 }
249
250 func (c *countWriter) Write(p []byte) (n int, err error) {
251 n, err = c.w.Write(p)
252 c.apkw.offset += n
253 return n, err
254 }
255
256 type fileWriter struct {
257 name string
258 w io.Writer
259 sha1 hash.Hash
260 closed bool
261 }
262
263 func (w *fileWriter) Write(p []byte) (n int, err error) {
264 if w.closed {
265 return 0, fmt.Errorf("apk: write to closed file %q", w.name)
266 }
267 w.sha1.Write(p)
268 n, err = w.w.Write(p)
269 if err != nil {
270 err = fmt.Errorf("apk: %v", err)
271 }
272 return n, err
273 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698