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