ethereum / go-ethereum

Go implementation of the Ethereum protocol
https://geth.ethereum.org
GNU Lesser General Public License v3.0
47.16k stars 19.96k forks source link

Implement code signing on macOS and Windows #18291

Open fjl opened 5 years ago

fjl commented 5 years ago

Ethereum Foundation has developer accounts with Apple and Microsoft. The Mist project uses those to code-sign executables. We should start shipping code-signed executables for those platforms.

FrankSzendzielarz commented 5 years ago

Windows AppVeyor code signing guide:

To codesign (Authenticode) the Windows geth executable, the most common option is to use the Windows SDK "Signtool" program.

Geth (and other exes) are built for Windows using Appveyor, the configuration for which is specified by an Appveyor.yml file in the project source.

(The appveyor.yml file (goethereum\appveyor.yml) specifies an OS of "os: Visual Studio 2015" According to https://www.appveyor.com/docs/appveyor-yml/ "os:" is an invalid tag (should be "image:") but elsewhere it is stated that "Visual Studio 2015" is currently the default image when not specified.)

According to online references the location of signtool.exe on the appveyor machine should be C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Bin\signtool.exe . It is not clear if this location is the appveyor PATH. The below yml file has options for either case.

The aim is to use Signtool to sign the executable post-build. In a private environment, the syntaxes for this would be as follows.

SHA1 SignTool.exe sign /f EthereumCert.pfx /p EthereumCertPassword /t http://timestamp.verisign.com/scripts/timstamp.dll Geth.exe

SHA256 signtool.exe sign /f EthereumCert.pfx /p EthereumCertPassword /tr http://timestamp.geotrust.com/tsa /td sha256 /fd sha256 Geth.exe

where EthereumCert.pfx is a password protected Ethereum Foundation certificate, EthereumCertPassword is the password to that certificate, and the /t or /tr options (depending on algorithm) specify URIs to a timestamping service and indicate the signature should include a timestamp. The timestamp is necessary to determine that the executable was signed during the validity period of the certificate. Without this, code signatures become invalid as soon as the certificate expires.

An additional requirement though is that the EthereumCertPassword and pfx files cannot be supplied in plain text as part of an open source project.

The EF foundation pfx file should be converted to an AppVeyor "secure file". To do this the following steps are suggested by AppVeyor:

In summary, those steps describe downloading a tool from AppVeyor to encrypt the file with a secret, and then adding that secret to the AppVeyor build project either as an AppVeyor project setting or as a "secure variable" (the latter assumed below)

The EF foundation pfx password should also be added to the appveyor.yml file as a "secure variable". To do this, follow the instructions here

With the above steps complete, the appveyor.yml should be modified to look as follows. Modified lines are indicated with an indent. The geth project should include the encrypted certificate (or it could be downloaded from a secure location).

```
# Clone directly into GOPATH.
    clone_folder: C:\gopath\src\github.com\ethereum\go-ethereum
    clone_depth: 5
    version: "{branch}.{build}"
    environment:
      global:
        GOPATH: C:\gopath
        CC: gcc.exe
      enc_secret:
          secure: <cert encryption secret generated via appveyor>
      key_secret:
          secure: <cert password secret generated via appveyor>
      matrix:
        - GETH_ARCH: amd64
          MSYS2_ARCH: x86_64
          MSYS2_BITS: 64
          MSYSTEM: MINGW64
          PATH: C:\msys64\mingw64\bin\;C:\Program Files (x86)\NSIS\;%PATH%
        - GETH_ARCH: 386
          MSYS2_ARCH: i686
          MSYS2_BITS: 32
          MSYSTEM: MINGW32
          PATH: C:\msys64\mingw32\bin\;C:\Program Files (x86)\NSIS\;%PATH%

    install:
    - ps: iex ((New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/appveyor/secure-file/master/install.ps1'))
    - cmd: appveyor-tools\secure-file -decrypt <path-to\encrypted-filename.ext.enc> -secret %enc_secret%
    build_script:
      - go run build\ci.go install
    - ps: signtool sign /f <decrypted cert.pfx> /p $env:key_secret /tr http://timestamp.geotrust.com/tsa /td sha256 /fd sha256 <path to geth.exe>

[Or if signtool is not in the path then...]

    after_build:
      - go run build\ci.go archive -type zip -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
      - go run build\ci.go nsis -signer WINDOWS_SIGNING_KEY -upload gethstore/builds

    test_script:
      - set CGO_ENABLED=1
      - go run build\ci.go test -coverage