golang codesign 代码
文件路径:/src/cmd/internal/codesign/codesign.go
/ Copyright 2020 The Go Authors. All rights reserved.
/ Use of this source code is governed by a BSD-style
/ license that can be found in the LICENSE file.
/ Package codesign provides basic functionalities for
/ ad-hoc code signing of Mach-O files.
/
/ This is not a general tool for code-signing. It is made
/ specifically for the Go toolchain. It uses the same
/ ad-hoc signing algorithm as the Darwin linker.
package codesign
import (
"debug/macho"
"encoding/binary"
"io"
"cmd/internal/notsha256"
)
/ Code signature layout.
/
/ The code signature is a block of bytes that contains
/ a SuperBlob, which contains one or more Blobs. For ad-hoc
/ signing, a single CodeDirectory Blob suffices.
/
/ A SuperBlob starts with its header (the binary representation
/ of the SuperBlob struct), followed by a list of (in our case,
/ one) Blobs (offset and size). A CodeDirectory Blob starts
/ with its head (the binary representation of CodeDirectory struct),
/ followed by the identifier (as a C string) and the hashes, at
/ the corresponding offsets.
/
/ The signature data must be included in the __LINKEDIT segment.
/ In the Mach-O file header, an LC_CODE_SIGNATURE load command
/ points to the data.
const (
pageSizeBits = 12
pageSize = 1 << pageSizeBits
)
const LC_CODE_SIGNATURE = 0x1d
/ Constants and struct layouts are from
/ https://opensource.apple.com/source/xnu/xnu-4903.270.47/osfmk/kern/cs_blobs.h
const (
CSMAGIC_REQUIREMENT = 0xfade0c00 / single Requirement blob
CSMAGIC_REQUIREMENTS = 0xfade0c01 / Requirements vector (internal requirements)
CSMAGIC_CODEDIRECTORY = 0xfade0c02 / CodeDirectory blob
CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0 / embedded form of signature data
CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1 / multi-arch collection of embedded signatures
CSSLOT_CODEDIRECTORY = 0 / slot index for CodeDirectory
)
const (
CS_HASHTYPE_SHA1 = 1
CS_HASHTYPE_SHA256 = 2
CS_HASHTYPE_SHA256_TRUNCATED = 3
CS_HASHTYPE_SHA384 = 4
)
const (
CS_EXECSEG_MAIN_BINARY = 0x1 / executable segment denotes main binary
CS_EXECSEG_ALLOW_UNSIGNED = 0x10 / allow unsigned pages (for debugging)
CS_EXECSEG_DEBUGGER = 0x20 / main binary is debugger
CS_EXECSEG_JIT = 0x40 / JIT enabled
CS_EXECSEG_SKIP_LV = 0x80 / skip library validation
CS_EXECSEG_CAN_LOAD_CDHASH = 0x100 / can bless cdhash for execution
CS_EXECSEG_CAN_EXEC_CDHASH = 0x200 / can execute blessed cdhash
)
type Blob struct {
typ uint32 / type of entry
offset uint32 / offset of entry
/ data follows
}
func (b *Blob) put(out []byte) []byte {
out = put32be(out, b.typ)
out = put32be(out, b.offset)
return out
}
const blobSize = 2 * 4
type SuperBlob struct {
magic uint32 / magic number
length uint32 / total length of SuperBlob
count uint32 / number of index entries following
/ blobs []Blob
}
func (s *SuperBlob) put(out []byte) []byte {
out = put32be(out, s.magic)
out = put32be(out, s.length)
out = put32be(out, s.count)
return out
}
const superBlobSize = 3 * 4
type CodeDirectory struct {
magic uint32 / magic number (CSMAGIC_CODEDIRECTORY)
length uint32 / total length of CodeDirectory blob
version uint32 / compatibility version
flags uint32 / setup and mode flags
hashOffset uint32 / offset of hash slot element at index zero
identOffset uint32 / offset of identifier string
nSpecialSlots uint32 / number of special hash slots
nCodeSlots uint32 / number of ordinary (code) hash slots
codeLimit uint32 / limit to main image signature range
hashSize uint8 / size of each hash in bytes
hashType uint8 / type of hash (cdHashType* constants)
_pad1 uint8 / unused (must be zero)
pageSize uint8 / log2(page size in bytes); 0 => infinite
_pad2 uint32 / unused (must be zero)
scatterOffset uint32
teamOffset uint32
_pad3 uint32
codeLimit64 uint64
execSegBase uint64
execSegLimit uint64
execSegFlags uint64
/ data follows
}
func (c *CodeDirectory) put(out []byte) []byte {
out = put32be(out, c.magic)
out = put32be(out, c.length)
out = put32be(out, c.version)
out = put32be(out, c.flags)
out = put32be(out, c.hashOffset)
out = put32be(out, c.identOffset)
out = put32be(out, c.nSpecialSlots)
out = put32be(out, c.nCodeSlots)
out = put32be(out, c.codeLimit)
out = put8(out, c.hashSize)
out = put8(out, c.hashType)
out = put8(out, c._pad1)
out = put8(out, c.pageSize)
out = put32be(out, c._pad2)
out = put32be(out, c.scatterOffset)
out = put32be(out, c.teamOffset)
out = put32be(out, c._pad3)
out = put64be(out, c.codeLimit64)
out = put64be(out, c.execSegBase)
out = put64be(out, c.execSegLimit)
out = put64be(out, c.execSegFlags)
return out
}
const codeDirectorySize = 13*4 + 4 + 4*8
/ CodeSigCmd is Mach-O LC_CODE_SIGNATURE load command.
type CodeSigCmd struct {
Cmd uint32 / LC_CODE_SIGNATURE
Cmdsize uint32 / sizeof this command (16)
Dataoff uint32 / file offset of data in __LINKEDIT segment
Datasize uint32 / file size of data in __LINKEDIT segment
}
func FindCodeSigCmd(f *macho.File) (CodeSigCmd, bool) {
get32 := f.ByteOrder.Uint32
for _, l := range f.Loads {
data := l.Raw()
cmd := get32(data)
if cmd == LC_CODE_SIGNATURE {
return CodeSigCmd{
cmd,
get32(data[4:]),
get32(data[8:]),
get32(data[12:]),
}, true
}
}
return CodeSigCmd{}, false
}
func put32be(b []byte, x uint32) []byte { binary.BigEndian.PutUint32(b, x); return b[4:] }
func put64be(b []byte, x uint64) []byte { binary.BigEndian.PutUint64(b, x); return b[8:] }
func put8(b []byte, x uint8) []byte { b[0] = x; return b[1:] }
func puts(b, s []byte) []byte { n := copy(b, s); return b[n:] }
/ Size computes the size of the code signature.
/ id is the identifier used for signing (a field in CodeDirectory blob, which
/ has no significance in ad-hoc signing).
func Size(codeSize int64, id string) int64 {
nhashes := (codeSize + pageSize - 1) / pageSize
idOff := int64(codeDirectorySize)
hashOff := idOff + int64(len(id)+1)
cdirSz := hashOff + nhashes*notsha256.Size
return int64(superBlobSize+blobSize) + cdirSz
}
/ Sign generates an ad-hoc code signature and writes it to out.
/ out must have length at least Size(codeSize, id).
/ data is the file content without the signature, of size codeSize.
/ textOff and textSize is the file offset and size of the text segment.
/ isMain is true if this is a main executable.
/ id is the identifier used for signing (a field in CodeDirectory blob, which
/ has no significance in ad-hoc signing).
func Sign(out []byte, data io.Reader, id string, codeSize, textOff, textSize int64, isMain bool) {
nhashes := (codeSize + pageSize - 1) / pageSize
idOff := int64(codeDirectorySize)
hashOff := idOff + int64(len(id)+1)
sz := Size(codeSize, id)
/ emit blob headers
sb := SuperBlob{
magic: CSMAGIC_EMBEDDED_SIGNATURE,
length: uint32(sz),
count: 1,
}
blob := Blob{
typ: CSSLOT_CODEDIRECTORY,
offset: superBlobSize + blobSize,
}
cdir := CodeDirectory{
magic: CSMAGIC_CODEDIRECTORY,
length: uint32(sz) - (superBlobSize + blobSize),
version: 0x20400,
flags: 0x20002, / adhoc | linkerSigned
hashOffset: uint32(hashOff),
identOffset: uint32(idOff),
nCodeSlots: uint32(nhashes),
codeLimit: uint32(codeSize),
hashSize: notsha256.Size,
hashType: CS_HASHTYPE_SHA256,
pageSize: uint8(pageSizeBits),
execSegBase: uint64(textOff),
execSegLimit: uint64(textSize),
}
if isMain {
cdir.execSegFlags = CS_EXECSEG_MAIN_BINARY
}
outp := out
outp = sb.put(outp)
outp = blob.put(outp)
outp = cdir.put(outp)
/ emit the identifier
outp = puts(outp, []byte(id+"\000"))
/ emit hashes
/ NOTE(rsc): These must be SHA256, but for cgo bootstrap reasons
/ we cannot import crypto/sha256 when GOEXPERIMENT=boringcrypto
/ and the host is linux/amd64. So we use NOT-SHA256
/ and then apply a NOT ourselves to get SHA256. Sigh.
var buf [pageSize]byte
h := notsha256.New()
p := 0
for p < int(codeSize) {
n, err := io.ReadFull(data, buf[:])
if err == io.EOF {
break
}
if err != nil && err != io.ErrUnexpectedEOF {
panic(err)
}
if p+n > int(codeSize) {
n = int(codeSize) - p
}
p += n
h.Reset()
h.Write(buf[:n])
b := h.Sum(nil)
for i := range b {
b[i] ^= 0xFF / convert notsha256 to sha256
}
outp = puts(outp, b[:])
}
}