github.com/bir3/gocompiler@v0.9.2202/src/cmd/internal/codesign/codesign.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package codesign provides basic functionalities for 6 // ad-hoc code signing of Mach-O files. 7 // 8 // This is not a general tool for code-signing. It is made 9 // specifically for the Go toolchain. It uses the same 10 // ad-hoc signing algorithm as the Darwin linker. 11 package codesign 12 13 import ( 14 "debug/macho" 15 "encoding/binary" 16 "io" 17 18 "github.com/bir3/gocompiler/src/cmd/internal/notsha256" 19 ) 20 21 // Code signature layout. 22 // 23 // The code signature is a block of bytes that contains 24 // a SuperBlob, which contains one or more Blobs. For ad-hoc 25 // signing, a single CodeDirectory Blob suffices. 26 // 27 // A SuperBlob starts with its header (the binary representation 28 // of the SuperBlob struct), followed by a list of (in our case, 29 // one) Blobs (offset and size). A CodeDirectory Blob starts 30 // with its head (the binary representation of CodeDirectory struct), 31 // followed by the identifier (as a C string) and the hashes, at 32 // the corresponding offsets. 33 // 34 // The signature data must be included in the __LINKEDIT segment. 35 // In the Mach-O file header, an LC_CODE_SIGNATURE load command 36 // points to the data. 37 38 const ( 39 pageSizeBits = 12 40 pageSize = 1 << pageSizeBits 41 ) 42 43 const LC_CODE_SIGNATURE = 0x1d 44 45 // Constants and struct layouts are from 46 // https://opensource.apple.com/source/xnu/xnu-4903.270.47/osfmk/kern/cs_blobs.h 47 48 const ( 49 CSMAGIC_REQUIREMENT = 0xfade0c00 // single Requirement blob 50 CSMAGIC_REQUIREMENTS = 0xfade0c01 // Requirements vector (internal requirements) 51 CSMAGIC_CODEDIRECTORY = 0xfade0c02 // CodeDirectory blob 52 CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0 // embedded form of signature data 53 CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1 // multi-arch collection of embedded signatures 54 55 CSSLOT_CODEDIRECTORY = 0 // slot index for CodeDirectory 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 // executable segment denotes main binary 67 CS_EXECSEG_ALLOW_UNSIGNED = 0x10 // allow unsigned pages (for debugging) 68 CS_EXECSEG_DEBUGGER = 0x20 // main binary is debugger 69 CS_EXECSEG_JIT = 0x40 // JIT enabled 70 CS_EXECSEG_SKIP_LV = 0x80 // skip library validation 71 CS_EXECSEG_CAN_LOAD_CDHASH = 0x100 // can bless cdhash for execution 72 CS_EXECSEG_CAN_EXEC_CDHASH = 0x200 // can execute blessed cdhash 73 ) 74 75 type Blob struct { 76 typ uint32 // type of entry 77 offset uint32 // offset of entry 78 // data follows 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 // magic number 91 length uint32 // total length of SuperBlob 92 count uint32 // number of index entries following 93 // blobs []Blob 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 // magic number (CSMAGIC_CODEDIRECTORY) 107 length uint32 // total length of CodeDirectory blob 108 version uint32 // compatibility version 109 flags uint32 // setup and mode flags 110 hashOffset uint32 // offset of hash slot element at index zero 111 identOffset uint32 // offset of identifier string 112 nSpecialSlots uint32 // number of special hash slots 113 nCodeSlots uint32 // number of ordinary (code) hash slots 114 codeLimit uint32 // limit to main image signature range 115 hashSize uint8 // size of each hash in bytes 116 hashType uint8 // type of hash (cdHashType* constants) 117 _pad1 uint8 // unused (must be zero) 118 pageSize uint8 // log2(page size in bytes); 0 => infinite 119 _pad2 uint32 // unused (must be zero) 120 scatterOffset uint32 121 teamOffset uint32 122 _pad3 uint32 123 codeLimit64 uint64 124 execSegBase uint64 125 execSegLimit uint64 126 execSegFlags uint64 127 // data follows 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 // CodeSigCmd is Mach-O LC_CODE_SIGNATURE load command. 158 type CodeSigCmd struct { 159 Cmd uint32 // LC_CODE_SIGNATURE 160 Cmdsize uint32 // sizeof this command (16) 161 Dataoff uint32 // file offset of data in __LINKEDIT segment 162 Datasize uint32 // file size of data in __LINKEDIT segment 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 // Size computes the size of the code signature. 188 // id is the identifier used for signing (a field in CodeDirectory blob, which 189 // has no significance in ad-hoc signing). 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 // Sign generates an ad-hoc code signature and writes it to out. 199 // out must have length at least Size(codeSize, id). 200 // data is the file content without the signature, of size codeSize. 201 // textOff and textSize is the file offset and size of the text segment. 202 // isMain is true if this is a main executable. 203 // id is the identifier used for signing (a field in CodeDirectory blob, which 204 // has no significance in ad-hoc signing). 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 // emit blob headers 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, // adhoc | linkerSigned 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 // emit the identifier 246 outp = puts(outp, []byte(id+"\000")) 247 248 // emit hashes 249 // NOTE(rsc): These must be SHA256, but for cgo bootstrap reasons 250 // we cannot import crypto/sha256 when GOEXPERIMENT=boringcrypto 251 // and the host is linux/amd64. So we use NOT-SHA256 252 // and then apply a NOT ourselves to get SHA256. Sigh. 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 // convert notsha256 to sha256 273 } 274 outp = puts(outp, b[:]) 275 } 276 }