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  }