| | |
| | |
| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | package codesign |
| |
|
| | import ( |
| | "crypto/sha256" |
| | "debug/macho" |
| | "encoding/binary" |
| | "io" |
| |
|
| | "cmd/internal/hash" |
| | ) |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | const ( |
| | pageSizeBits = 12 |
| | pageSize = 1 << pageSizeBits |
| | ) |
| |
|
| | const LC_CODE_SIGNATURE = 0x1d |
| |
|
| | |
| | |
| |
|
| | const ( |
| | CSMAGIC_REQUIREMENT = 0xfade0c00 |
| | CSMAGIC_REQUIREMENTS = 0xfade0c01 |
| | CSMAGIC_CODEDIRECTORY = 0xfade0c02 |
| | CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0 |
| | CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1 |
| |
|
| | CSSLOT_CODEDIRECTORY = 0 |
| | ) |
| |
|
| | const ( |
| | CS_HASHTYPE_SHA1 = 1 |
| | CS_HASHTYPE_SHA256 = 2 |
| | CS_HASHTYPE_SHA256_TRUNCATED = 3 |
| | CS_HASHTYPE_SHA384 = 4 |
| | ) |
| |
|
| | const ( |
| | CS_EXECSEG_MAIN_BINARY = 0x1 |
| | CS_EXECSEG_ALLOW_UNSIGNED = 0x10 |
| | CS_EXECSEG_DEBUGGER = 0x20 |
| | CS_EXECSEG_JIT = 0x40 |
| | CS_EXECSEG_SKIP_LV = 0x80 |
| | CS_EXECSEG_CAN_LOAD_CDHASH = 0x100 |
| | CS_EXECSEG_CAN_EXEC_CDHASH = 0x200 |
| | ) |
| |
|
| | type Blob struct { |
| | typ uint32 |
| | offset uint32 |
| | |
| | } |
| |
|
| | 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 |
| | length uint32 |
| | count uint32 |
| | |
| | } |
| |
|
| | 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 |
| | length uint32 |
| | version uint32 |
| | flags uint32 |
| | hashOffset uint32 |
| | identOffset uint32 |
| | nSpecialSlots uint32 |
| | nCodeSlots uint32 |
| | codeLimit uint32 |
| | hashSize uint8 |
| | hashType uint8 |
| | _pad1 uint8 |
| | pageSize uint8 |
| | _pad2 uint32 |
| | scatterOffset uint32 |
| | teamOffset uint32 |
| | _pad3 uint32 |
| | codeLimit64 uint64 |
| | execSegBase uint64 |
| | execSegLimit uint64 |
| | execSegFlags uint64 |
| | |
| | } |
| |
|
| | 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 |
| |
|
| | |
| | type CodeSigCmd struct { |
| | Cmd uint32 |
| | Cmdsize uint32 |
| | Dataoff uint32 |
| | Datasize uint32 |
| | } |
| |
|
| | 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:] } |
| |
|
| | |
| | |
| | |
| | func Size(codeSize int64, id string) int64 { |
| | nhashes := (codeSize + pageSize - 1) / pageSize |
| | idOff := int64(codeDirectorySize) |
| | hashOff := idOff + int64(len(id)+1) |
| | cdirSz := hashOff + nhashes*hash.Size32 |
| | return int64(superBlobSize+blobSize) + cdirSz |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | 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) |
| |
|
| | |
| | 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, |
| | hashOffset: uint32(hashOff), |
| | identOffset: uint32(idOff), |
| | nCodeSlots: uint32(nhashes), |
| | codeLimit: uint32(codeSize), |
| | hashSize: hash.Size32, |
| | 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) |
| |
|
| | |
| | outp = puts(outp, []byte(id+"\000")) |
| |
|
| | |
| | var buf [pageSize]byte |
| | 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 |
| | b := sha256.Sum256(buf[:n]) |
| | outp = puts(outp, b[:]) |
| | } |
| | } |
| |
|