-
Notifications
You must be signed in to change notification settings - Fork 3
/
writer.go
121 lines (114 loc) · 3.5 KB
/
writer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package passbook
import (
"archive/zip"
"bytes"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/hex"
"encoding/json"
"errors"
"io"
"path"
"github.com/mdigger/pkcs7sign"
)
var ErrNoPass = errors.New("pass.json missed") // the description of pass.json was not added to the file
// Writer allows you to write files in Apple Passbook format.
type Writer struct {
zip *zip.Writer // Packer
cert *x509.Certificate // Certificate used for signature
priv *rsa.PrivateKey // Private key used for signature
hasPass bool // Flag that description of passbook added
manifest map[string]string // Hash of files
}
// NewWriter creates a new Writer that allows you to create an Apple Passbook file.
// As parameters, a stream is passed to which the given file will be written,
// as well as the certificates that will be used to create the digital signature.
func NewWriter(out io.Writer, cert *x509.Certificate, priv *rsa.PrivateKey) *Writer {
return &Writer{
zip: zip.NewWriter(out), // сжимаем при записи
cert: cert,
priv: priv,
manifest: make(map[string]string),
}
}
// Close finishes writing an Apple Passbook file and adds it automatically
// generated manifest and signature file. At the time of creating a digital signature,
// error, which in this case will also be returned. In addition, the error will return if
// the description of pass.json was not added to the file.
func (w *Writer) Close() (err error) {
if w.zip == nil {
return nil
}
// Do not forget to close the compression in any case
defer func() {
if closeErr := w.zip.Close(); err == nil && closeErr != nil {
err = closeErr
}
w.zip = nil
}()
// Check that the main description has been added
if !w.hasPass {
return ErrNoPass
}
// Translate the manifest into JSON
manifestData, err := json.MarshalIndent(w.manifest, "", "\t")
if err != nil {
return err
}
// We record the manifest data
manifestWriter, err := w.zip.Create("manifest.json")
if err != nil {
return err
}
if _, err = manifestWriter.Write(manifestData); err != nil {
return err
}
// Create a signature
signature, err := pkcs7.Sign(bytes.NewReader(manifestData), w.cert, w.priv)
if err != nil {
return err
}
// Write the signature to a file
signatureWriter, err := w.zip.Create("signature")
if err != nil {
return err
}
if _, err = signatureWriter.Write(signature); err != nil {
return err
}
return nil
}
// Add adds a new file to the Passbook. Only files with the extension .png and .strings are added.
// Plus, a file called pass.json is added, which is a direct description.
// All other files are ignored.
func (w *Writer) Add(name string, r io.Reader) error {
if w.zip == nil {
return io.ErrClosedPipe // write stream closed
}
// Ignore unhandled files
switch path.Ext(name) {
case ".json": // From json-files we add only the description directly
if name != "pass.json" {
return nil
}
case ".png": // picture
case ".strings": // localization
default: // Everything else is ignored
return nil
}
zipw, err := w.zip.Create(name) // Create a new file in the archive
if err != nil {
return err
}
hash := sha1.New() // Initialize hash counting
// At the same time we write to the archive and consider a hash
if _, err := io.Copy(io.MultiWriter(zipw, hash), r); err != nil {
return err
}
w.manifest[name] = hex.EncodeToString(hash.Sum(nil)) // Save received hash
if name == "pass.json" {
w.hasPass = true // Save the flag that the main description is added
}
return nil
}