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