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