Documentation
¶
Overview ¶
Package clientbuildtest builds the fixtures the client-building tests need: a file shaped like a Windows executable, with a reserved region inside it.
It is a fixture and not a real client on purpose. A real one is eleven megabytes of another repository's build output, which no test in this one can produce; this is a few kilobytes, it is deterministic, a test can put the region anywhere inside it, and debug/pe parses it. The real article is exercised end to end against a client built by wails3.
The layout below is spelled out from the format in the package comment of clientbuild rather than imported from it. A fixture that reused the code under test could only ever prove that code agrees with itself, and the client at the other end of this format has its own, separate implementation of exactly these offsets.
Index ¶
- Constants
- func BlankRegion() []byte
- func Certificate(tb testing.TB, opts CertificateOptions) ([]byte, *x509.Certificate)
- func Client(region []byte) []byte
- func Ed25519Certificate(tb testing.TB) []byte
- func Head() []byte
- func Install(tb testing.TB) string
- func MultiSectionPE(region []byte) []byte
- func PE(body []byte) []byte
- func ShuffleSectionTable(body []byte) []byte
- func StampedRegion(payload string) []byte
- func Tail() []byte
- func WriteAt(tb testing.TB, path string, body []byte) string
- type Authority
- type CertificateOptions
Constants ¶
const ( // RegionSize is the whole reserved region. RegionSize = 1024 // MarkerSize is the length of each marker. MarkerSize = 16 // OffVersion, OffReserved, OffLength, OffChecksum and OffPayload are the // header fields between the head marker and the payload. OffVersion = 16 OffReserved = 17 OffLength = 18 OffChecksum = 20 OffPayload = 24 // OffTail is where the tail marker starts. OffTail = RegionSize - MarkerSize // Version1 is the format version a stamped region carries. Version1 = 1 )
The region, as the format describes it.
const FileAlignment = 0x200
FileAlignment is what a section's bytes on disk are rounded up to. It is the smallest value a PE may declare, which keeps the fixture small.
const TestKeyBits = 2048
TestKeyBits is the RSA key size the fixtures use. It is small on purpose: these keys sign a fixture and are thrown away at the end of the test, and three thousand bits would put a second on every run that uses one.
Variables ¶
This section is empty.
Functions ¶
func BlankRegion ¶
func BlankRegion() []byte
BlankRegion is the region as a prebuilt client carries it: the two markers, and room between them.
func Certificate ¶
func Certificate(tb testing.TB, opts CertificateOptions) ([]byte, *x509.Certificate)
Certificate mints a certificate and returns the PKCS#12 file holding it and its key — the file an operator uploads — along with the certificate itself for a test that wants to assert about what is inside.
func Client ¶
Client is a fixture client: filler, a region, more filler, all inside a PE. The filler is there so that a test asserting "nothing outside the region changed" has something outside the region to assert about.
func Ed25519Certificate ¶
Ed25519Certificate is a certificate holding a key that is fine everywhere except here: Authenticode names RSA and ECDSA and nothing else, so a server that accepted one would make signatures no Windows machine can check.
func Install ¶
Install writes a stampable client into a directory of its own and returns its path, which is what a test wanting "a server that has a client" needs.
func MultiSectionPE ¶
MultiSectionPE is a fixture shaped more like a real client than PE is: several sections, and debug data after the last of them that belongs to no section at all.
Both of those are places the Authenticode hash can go wrong in a way one section cannot show: the sections have to be hashed one after another with their alignment padding, and the bytes after the last of them have to be hashed too — leaving them out would let somebody append to a signed file without breaking the signature.
The section table is written in file order, which is what every linker does. ShuffleSectionTable makes one that is not, for the test that the hash follows the file rather than the table.
func PE ¶
PE wraps bytes in the smallest thing that is a valid Windows executable: a DOS header, a PE header, one section, and the bytes as that section's data, padded out to the alignment the header declares.
func ShuffleSectionTable ¶
ShuffleSectionTable reverses the order of the rows in a PE's section table, leaving every other byte of the file alone.
The file it returns is the same program: the sections are where they were and hold what they held, and only the order they are listed in has changed. The Authenticode hash is defined over the sections in the order they sit in the file, so a signature over this file and a signature over the original must be the same signature. A hash that read the table in order instead would produce a different one, and that is what the test using this catches.
func StampedRegion ¶
StampedRegion is a region with a payload written into it by hand, which is how the tests build the cases that are wrong in one specific way: take this, and break one field of it.
Types ¶
type Authority ¶
type Authority struct {
Certificate *x509.Certificate
Key *rsa.PrivateKey
}
Authority is a certificate authority the fixtures can issue under, for the tests that care about the difference between a certificate an operator issued to themselves and one issued to them.
type CertificateOptions ¶
type CertificateOptions struct {
// CommonName is who the certificate says signed. Empty is a usable
// default rather than a certificate with no name.
CommonName string
// NotBefore and NotAfter are its window. Zero values are an hour ago and a
// year from now.
NotBefore, NotAfter time.Time
// NoCodeSigning mints one without the extended key usage that marks it for
// signing software — the certificate an operator exports from the wrong
// place, which signs fine and which Windows refuses.
NoCodeSigning bool
// ECDSA mints an elliptic-curve key instead of RSA.
ECDSA bool
// Issuer is the authority to issue under. Nil is self-signed, which is
// what an operator making their own certificate has.
Issuer *Authority
// Password is what the PKCS#12 file is locked with.
Password string
}
CertificateOptions is what to mint. The zero value is a plain self-signed code-signing certificate that is valid now, which is what most tests want.