1
2
3
4
5
6
7
8
9
10
11 package codesign
12
13 import (
14 "crypto/sha256"
15 "debug/macho"
16 "encoding/binary"
17 "io"
18 )
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 const (
38 pageSizeBits = 12
39 pageSize = 1 << pageSizeBits
40 )
41
42 const LC_CODE_SIGNATURE = 0x1d
43
44
45
46
47 const (
48 CSMAGIC_REQUIREMENT = 0xfade0c00
49 CSMAGIC_REQUIREMENTS = 0xfade0c01
50 CSMAGIC_CODEDIRECTORY = 0xfade0c02
51 CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0
52 CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1
53
54 CSSLOT_CODEDIRECTORY = 0
55 )
56
57 const (
58 CS_HASHTYPE_SHA1 = 1
59 CS_HASHTYPE_SHA256 = 2
60 CS_HASHTYPE_SHA256_TRUNCATED = 3
61 CS_HASHTYPE_SHA384 = 4
62 )
63
64 const (
65 CS_EXECSEG_MAIN_BINARY = 0x1
66 CS_EXECSEG_ALLOW_UNSIGNED = 0x10
67 CS_EXECSEG_DEBUGGER = 0x20
68 CS_EXECSEG_JIT = 0x40
69 CS_EXECSEG_SKIP_LV = 0x80
70 CS_EXECSEG_CAN_LOAD_CDHASH = 0x100
71 CS_EXECSEG_CAN_EXEC_CDHASH = 0x200
72 )
73
74 type Blob struct {
75 typ uint32
76 offset uint32
77
78 }
79
80 func (b *Blob) put(out []byte) []byte {
81 out = put32be(out, b.typ)
82 out = put32be(out, b.offset)
83 return out
84 }
85
86 const blobSize = 2 * 4
87
88 type SuperBlob struct {
89 magic uint32
90 length uint32
91 count uint32
92
93 }
94
95 func (s *SuperBlob) put(out []byte) []byte {
96 out = put32be(out, s.magic)
97 out = put32be(out, s.length)
98 out = put32be(out, s.count)
99 return out
100 }
101
102 const superBlobSize = 3 * 4
103
104 type CodeDirectory struct {
105 magic uint32
106 length uint32
107 version uint32
108 flags uint32
109 hashOffset uint32
110 identOffset uint32
111 nSpecialSlots uint32
112 nCodeSlots uint32
113 codeLimit uint32
114 hashSize uint8
115 hashType uint8
116 _pad1 uint8
117 pageSize uint8
118 _pad2 uint32
119 scatterOffset uint32
120 teamOffset uint32
121 _pad3 uint32
122 codeLimit64 uint64
123 execSegBase uint64
124 execSegLimit uint64
125 execSegFlags uint64
126
127 }
128
129 func (c *CodeDirectory) put(out []byte) []byte {
130 out = put32be(out, c.magic)
131 out = put32be(out, c.length)
132 out = put32be(out, c.version)
133 out = put32be(out, c.flags)
134 out = put32be(out, c.hashOffset)
135 out = put32be(out, c.identOffset)
136 out = put32be(out, c.nSpecialSlots)
137 out = put32be(out, c.nCodeSlots)
138 out = put32be(out, c.codeLimit)
139 out = put8(out, c.hashSize)
140 out = put8(out, c.hashType)
141 out = put8(out, c._pad1)
142 out = put8(out, c.pageSize)
143 out = put32be(out, c._pad2)
144 out = put32be(out, c.scatterOffset)
145 out = put32be(out, c.teamOffset)
146 out = put32be(out, c._pad3)
147 out = put64be(out, c.codeLimit64)
148 out = put64be(out, c.execSegBase)
149 out = put64be(out, c.execSegLimit)
150 out = put64be(out, c.execSegFlags)
151 return out
152 }
153
154 const codeDirectorySize = 13*4 + 4 + 4*8
155
156
157 type CodeSigCmd struct {
158 Cmd uint32
159 Cmdsize uint32
160 Dataoff uint32
161 Datasize uint32
162 }
163
164 func FindCodeSigCmd(f *macho.File) (CodeSigCmd, bool) {
165 get32 := f.ByteOrder.Uint32
166 for _, l := range f.Loads {
167 data := l.Raw()
168 cmd := get32(data)
169 if cmd == LC_CODE_SIGNATURE {
170 return CodeSigCmd{
171 cmd,
172 get32(data[4:]),
173 get32(data[8:]),
174 get32(data[12:]),
175 }, true
176 }
177 }
178 return CodeSigCmd{}, false
179 }
180
181 func put32be(b []byte, x uint32) []byte { binary.BigEndian.PutUint32(b, x); return b[4:] }
182 func put64be(b []byte, x uint64) []byte { binary.BigEndian.PutUint64(b, x); return b[8:] }
183 func put8(b []byte, x uint8) []byte { b[0] = x; return b[1:] }
184 func puts(b, s []byte) []byte { n := copy(b, s); return b[n:] }
185
186
187
188
189 func Size(codeSize int64, id string) int64 {
190 nhashes := (codeSize + pageSize - 1) / pageSize
191 idOff := int64(codeDirectorySize)
192 hashOff := idOff + int64(len(id)+1)
193 cdirSz := hashOff + nhashes*sha256.Size
194 return int64(superBlobSize+blobSize) + cdirSz
195 }
196
197
198
199
200
201
202
203
204 func Sign(out []byte, data io.Reader, id string, codeSize, textOff, textSize int64, isMain bool) {
205 nhashes := (codeSize + pageSize - 1) / pageSize
206 idOff := int64(codeDirectorySize)
207 hashOff := idOff + int64(len(id)+1)
208 sz := Size(codeSize, id)
209
210
211 sb := SuperBlob{
212 magic: CSMAGIC_EMBEDDED_SIGNATURE,
213 length: uint32(sz),
214 count: 1,
215 }
216 blob := Blob{
217 typ: CSSLOT_CODEDIRECTORY,
218 offset: superBlobSize + blobSize,
219 }
220 cdir := CodeDirectory{
221 magic: CSMAGIC_CODEDIRECTORY,
222 length: uint32(sz) - (superBlobSize + blobSize),
223 version: 0x20400,
224 flags: 0x20002,
225 hashOffset: uint32(hashOff),
226 identOffset: uint32(idOff),
227 nCodeSlots: uint32(nhashes),
228 codeLimit: uint32(codeSize),
229 hashSize: sha256.Size,
230 hashType: CS_HASHTYPE_SHA256,
231 pageSize: uint8(pageSizeBits),
232 execSegBase: uint64(textOff),
233 execSegLimit: uint64(textSize),
234 }
235 if isMain {
236 cdir.execSegFlags = CS_EXECSEG_MAIN_BINARY
237 }
238
239 outp := out
240 outp = sb.put(outp)
241 outp = blob.put(outp)
242 outp = cdir.put(outp)
243
244
245 outp = puts(outp, []byte(id+"\000"))
246
247
248 var buf [pageSize]byte
249 h := sha256.New()
250 p := 0
251 for p < int(codeSize) {
252 n, err := io.ReadFull(data, buf[:])
253 if err == io.EOF {
254 break
255 }
256 if err != nil && err != io.ErrUnexpectedEOF {
257 panic(err)
258 }
259 if p+n > int(codeSize) {
260 n = int(codeSize) - p
261 }
262 p += n
263 h.Reset()
264 h.Write(buf[:n])
265 b := h.Sum(nil)
266 outp = puts(outp, b[:])
267 }
268 }
269
View as plain text