Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

crypto: signing builds with signify/minisign #21798

Merged
merged 25 commits into from
Nov 27, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
419369d
internal/build: implement signify's signing func
gballet Nov 6, 2020
20331e9
Add signify to the ci utility
gballet Nov 8, 2020
ea463a2
fix output file format
gballet Nov 8, 2020
0028e1d
Add unit test for signify
gballet Nov 8, 2020
d875837
holiman's + travis' feedback
gballet Nov 16, 2020
9fb07a8
internal/build: verify signify's output
gballet Nov 17, 2020
c4fee15
crypto: move signify to common dir
gballet Nov 17, 2020
5eb3540
use go-minisign to verify binaries
gballet Nov 17, 2020
80d45d0
more holiman feedback
gballet Nov 18, 2020
3e1f47c
crypto, ci: support minisign output
gballet Nov 18, 2020
f035c15
only accept one-line trusted comments
gballet Nov 18, 2020
80c928c
configurable untrusted comments
gballet Nov 18, 2020
951d75c
code cleanup in tests
gballet Nov 18, 2020
68f8638
revert to use ed25519 from the stdlib
gballet Nov 20, 2020
6726a72
bug: fix for empty untrusted comments
gballet Nov 23, 2020
76886da
write timestamp as comment if trusted comment isn't present
gballet Nov 23, 2020
3fe9eb6
rename line checker to commentHasManyLines
gballet Nov 23, 2020
dfac93a
crypto: added signify fuzzer (#6)
MariusVanDerWijden Nov 23, 2020
b2cf7c6
extract key data inside a single function
gballet Nov 23, 2020
76db596
don't treat \r as a newline
gballet Nov 23, 2020
f92a450
travis: fix signing command line
gballet Nov 24, 2020
6fa7fc6
do not use an external binary in tests
gballet Nov 24, 2020
40b8b5f
crypto: move signify to crypto/signify
gballet Nov 24, 2020
698af71
travis: fix formatting issue
gballet Nov 27, 2020
b22cdb2
ci: fix linter build after package move
gballet Nov 27, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ jobs:
- fakeroot
- python-bzrlib
- python-paramiko
- signify-openbsd
script:
- echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts
- go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>"
Expand All @@ -64,25 +65,32 @@ jobs:
apt:
packages:
- gcc-multilib
- signify-openbsd
script:
# Build for the primary platforms that Trusty can manage
- go run build/ci.go install -dlgo
- go run build/ci.go archive -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
- go run build/ci.go archive -type tar -signer LINUX_SIGNIFY_KEY -upload gethstore/builds
- go run build/ci.go install -dlgo -arch 386
- go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
- go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNIFY_KEY -upload gethstore/builds

# Switch over GCC to cross compilation (breaks 386, hence why do it here only)
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-arm-linux-gnueabihf libc6-dev-armhf-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross
- sudo ln -s /usr/include/asm-generic /usr/include/asm

- GOARM=5 go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc
- GOARM=5 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
- GOARM=5 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNIFY_KEY -upload gethstore/builds
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix those lines up.

- GOARM=6 go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabi-gcc
- GOARM=6 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
- GOARM=6 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNIFY_KEY -upload gethstore/builds
- GOARM=7 go run build/ci.go install -dlgo -arch arm -cc arm-linux-gnueabihf-gcc
- GOARM=7 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
- GOARM=7 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNIFY_KEY -upload gethstore/builds
- go run build/ci.go install -dlgo -arch arm64 -cc aarch64-linux-gnu-gcc
- go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
- go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNIFY_KEY -upload gethstore/builds

# This builder does the Linux Azure MIPS xgo uploads
- stage: build
Expand All @@ -101,18 +109,22 @@ jobs:
- go run build/ci.go xgo --alltools -- --targets=linux/mips --ldflags '-extldflags "-static"' -v
- for bin in build/bin/*-linux-mips; do mv -f "${bin}" "${bin/-linux-mips/}"; done
- go run build/ci.go archive -arch mips -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
- go run build/ci.go archive -arch mips -type tar -signer LINUX_SIGNIFY_KEY -upload gethstore/builds

- go run build/ci.go xgo --alltools -- --targets=linux/mipsle --ldflags '-extldflags "-static"' -v
- for bin in build/bin/*-linux-mipsle; do mv -f "${bin}" "${bin/-linux-mipsle/}"; done
- go run build/ci.go archive -arch mipsle -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
- go run build/ci.go archive -arch mipsle -type tar -signer LINUX_SIGNIFY_KEY -upload gethstore/builds

- go run build/ci.go xgo --alltools -- --targets=linux/mips64 --ldflags '-extldflags "-static"' -v
- for bin in build/bin/*-linux-mips64; do mv -f "${bin}" "${bin/-linux-mips64/}"; done
- go run build/ci.go archive -arch mips64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
- go run build/ci.go archive -arch mips64 -type tar -signer LINUX_SIGNIFY_KEY -upload gethstore/builds

- go run build/ci.go xgo --alltools -- --targets=linux/mips64le --ldflags '-extldflags "-static"' -v
- for bin in build/bin/*-linux-mips64le; do mv -f "${bin}" "${bin/-linux-mips64le/}"; done
- go run build/ci.go archive -arch mips64le -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
- go run build/ci.go archive -arch mips64le -type tar -signer LINUX_SIGNIFY_KEY -upload gethstore/builds

# This builder does the Android Maven and Azure uploads
- stage: build
Expand Down Expand Up @@ -152,6 +164,7 @@ jobs:
- mkdir -p $GOPATH/src/github.com/ethereum
- ln -s `pwd` $GOPATH/src/github.com/ethereum/go-ethereum
- go run build/ci.go aar -signer ANDROID_SIGNING_KEY -deploy https://oss.sonatype.org -upload gethstore/builds
- go run build/ci.go aar -signer ANDROID_SIGNIFY_KEY -deploy https://oss.sonatype.org -upload gethstore/builds

# This builder does the OSX Azure, iOS CocoaPods and iOS Azure uploads
- stage: build
Expand All @@ -168,6 +181,7 @@ jobs:
script:
- go run build/ci.go install -dlgo
- go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -upload gethstore/builds
- go run build/ci.go archive -type tar -signer OSX_SIGNIFY_KEY -upload gethstore/builds

# Build the iOS framework and upload it to CocoaPods and Azure
- gem uninstall cocoapods -a -x
Expand All @@ -183,6 +197,7 @@ jobs:
# Workaround for https://github.com/golang/go/issues/23749
- export CGO_CFLAGS_ALLOW='-fmodules|-fblocks|-fobjc-arc'
- go run build/ci.go xcode -signer IOS_SIGNING_KEY -deploy trunk -upload gethstore/builds
- go run build/ci.go xcode -signer IOS_SIGNIFY_KEY -deploy trunk -upload gethstore/builds

# These builders run the tests
- stage: build
Expand Down
51 changes: 33 additions & 18 deletions build/ci.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,11 +396,12 @@ func downloadLinter(cachedir string) string {
// Release Packaging
func doArchive(cmdline []string) {
var (
arch = flag.String("arch", runtime.GOARCH, "Architecture cross packaging")
atype = flag.String("type", "zip", "Type of archive to write (zip|tar)")
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. LINUX_SIGNING_KEY)`)
upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
ext string
arch = flag.String("arch", runtime.GOARCH, "Architecture cross packaging")
atype = flag.String("type", "zip", "Type of archive to write (zip|tar)")
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. LINUX_SIGNING_KEY)`)
signify = flag.String("signify", "", `Environment variable holding the signify key (e.g. LINUX_SIGNIFY_KEY)`)
upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
ext string
)
flag.CommandLine.Parse(cmdline)
switch *atype {
Expand All @@ -427,7 +428,7 @@ func doArchive(cmdline []string) {
log.Fatal(err)
}
for _, archive := range []string{geth, alltools} {
if err := archiveUpload(archive, *upload, *signer); err != nil {
if err := archiveUpload(archive, *upload, *signer, *signify); err != nil {
log.Fatal(err)
}
}
Expand All @@ -447,14 +448,20 @@ func archiveBasename(arch string, archiveVersion string) string {
return platform + "-" + archiveVersion
}

func archiveUpload(archive string, blobstore string, signer string) error {
func archiveUpload(archive string, blobstore string, signer string, signify string) error {
// If signing was requested, generate the signature files
if signer != "" {
key := getenvBase64(signer)
if err := build.PGPSignFile(archive, archive+".asc", string(key)); err != nil {
return err
}
}
if signify != "" {
key := getenvBase64(string(signify))
if err := build.SignifySignFile(archive, archive+".sig", string(key)); err != nil {
return err
}
}
gballet marked this conversation as resolved.
Show resolved Hide resolved
// If uploading to Azure was requested, push the archive possibly with its signature
if blobstore != "" {
auth := build.AzureBlobstoreConfig{
Expand All @@ -470,6 +477,11 @@ func archiveUpload(archive string, blobstore string, signer string) error {
return err
}
}
if signify != "" {
if err := build.AzureBlobstoreUpload(archive+".sig", filepath.Base(archive+".sig"), auth); err != nil {
return err
}
}
}
return nil
}
Expand Down Expand Up @@ -806,6 +818,7 @@ func doWindowsInstaller(cmdline []string) {
var (
arch = flag.String("arch", runtime.GOARCH, "Architecture for cross build packaging")
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. WINDOWS_SIGNING_KEY)`)
signify = flag.String("signify key", "", `Environment variable holding the signify signing key (e.g. WINDOWS_SIGNIFY_KEY)`)
upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
workdir = flag.String("workdir", "", `Output directory for packages (uses temp dir if unset)`)
)
Expand Down Expand Up @@ -867,7 +880,7 @@ func doWindowsInstaller(cmdline []string) {
filepath.Join(*workdir, "geth.nsi"),
)
// Sign and publish installer.
if err := archiveUpload(installer, *upload, *signer); err != nil {
if err := archiveUpload(installer, *upload, *signer, *signify); err != nil {
log.Fatal(err)
}
}
Expand All @@ -876,10 +889,11 @@ func doWindowsInstaller(cmdline []string) {

func doAndroidArchive(cmdline []string) {
var (
local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`)
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. ANDROID_SIGNING_KEY)`)
deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "https://oss.sonatype.org")`)
upload = flag.String("upload", "", `Destination to upload the archive (usually "gethstore/builds")`)
local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`)
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. ANDROID_SIGNING_KEY)`)
signify = flag.String("signify", "", `Environment variable holding the signify signing key (e.g. ANDROID_SIGNIFY_KEY)`)
deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "https://oss.sonatype.org")`)
upload = flag.String("upload", "", `Destination to upload the archive (usually "gethstore/builds")`)
)
flag.CommandLine.Parse(cmdline)
env := build.Env()
Expand Down Expand Up @@ -908,7 +922,7 @@ func doAndroidArchive(cmdline []string) {
archive := "geth-" + archiveBasename("android", params.ArchiveVersion(env.Commit)) + ".aar"
os.Rename("geth.aar", archive)

if err := archiveUpload(archive, *upload, *signer); err != nil {
if err := archiveUpload(archive, *upload, *signer, *signify); err != nil {
log.Fatal(err)
}
// Sign and upload all the artifacts to Maven Central
Expand Down Expand Up @@ -1001,10 +1015,11 @@ func newMavenMetadata(env build.Environment) mavenMetadata {

func doXCodeFramework(cmdline []string) {
var (
local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`)
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. IOS_SIGNING_KEY)`)
deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "trunk")`)
upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`)
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. IOS_SIGNING_KEY)`)
signify = flag.String("signify", "", `Environment variable holding the signify signing key (e.g. IOS_SIGNIFY_KEY)`)
deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "trunk")`)
upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
)
flag.CommandLine.Parse(cmdline)
env := build.Env()
Expand Down Expand Up @@ -1032,7 +1047,7 @@ func doXCodeFramework(cmdline []string) {
maybeSkipArchive(env)

// Sign and upload the framework to Azure
if err := archiveUpload(archive+".tar.gz", *upload, *signer); err != nil {
if err := archiveUpload(archive+".tar.gz", *upload, *signer, *signify); err != nil {
log.Fatal(err)
}
// Prepare and upload a PodSpec to CocoaPods
Expand Down
85 changes: 85 additions & 0 deletions internal/build/signify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2020 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

// signFile reads the contents of an input file and signs it (in armored format)
// with the key provided, placing the signature into the output file.

package build

import (
"encoding/base64"
"errors"
"fmt"
"io/ioutil"
"os"

"crypto/ed25519"
)

var (
errInvalidKeyHeader = errors.New("Incorrect key header")
errInvalidKeyLength = errors.New("invalid, key length != 42")
)

func readSKey(key []byte) (ed25519.PrivateKey, error) {
if len(key) != 104 {
return nil, errInvalidKeyLength
}

if string(key[:2]) != "Ed" {
return nil, errInvalidKeyHeader
}

return ed25519.PrivateKey(key[40:]), nil

}

// SignifySignFile creates a signature of the input file.
func SignifySignFile(input string, output string, key string) error {
in, err := os.Open(input)
if err != nil {
return err
}
defer in.Close()

out, err := os.Create(output)
if err != nil {
return err
}
defer out.Close()

keydata, err := base64.StdEncoding.DecodeString(key)
if err != nil {
return err
}
skey, err := readSKey(keydata)
if err != nil {
return err
}

filedata, err := ioutil.ReadAll(in)
if err != nil {
return err
}

sigdata := []byte("Ed")
copy(sigdata, keydata[:2])
gballet marked this conversation as resolved.
Show resolved Hide resolved
sigdata = append(sigdata, keydata[32:40]...)
sigdata = append(sigdata, ed25519.Sign(skey, filedata)...)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, this looks really funky! At a glance, it looks like you're just happily appending the private key to the sigdata, which is then output in the message.
Not until one checks into readSKey, it becomes apparent that the private part after the first 40 bytes, which is not included here.

Perhaps you can make this more obvious, like

privKey := keydata[40:]
pubKey := keydata[32:40]
...
sigdata = append(sigdata, pubKey...)

Also, I don't quite understand -- what is sitting on bytes 2-32, which is totally ignored?

Copy link
Member Author

@gballet gballet Nov 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2-32 it contains data that is indicative of how the key got generated and also a checksum, which is not to be published in the signature.


out.WriteString(fmt.Sprintf("untrusted comment: verify with geth.pub\n%s\n", base64.StdEncoding.EncodeToString(sigdata)))
return nil
}
86 changes: 86 additions & 0 deletions internal/build/signify_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright 2020 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

// signFile reads the contents of an input file and signs it (in armored format)
// with the key provided, placing the signature into the output file.

package build

import (
"fmt"
"io/ioutil"
"math/rand"
"os"
"os/exec"
"runtime"
"testing"
"time"
)

var (
testSecKey = "RWRCSwAAAABVN5lr2JViGBN8DhX3/Qb/0g0wBdsNAR/APRW2qy9Fjsfr12sK2cd3URUFis1jgzQzaoayK8x4syT4G3Gvlt9RwGIwUYIQW/0mTeI+ECHu1lv5U4Wa2YHEPIesVPyRm5M="
testPubKey = "RWTAPRW2qy9FjsBiMFGCEFv9Jk3iPhAh7tZb+VOFmtmBxDyHrFT8kZuT"
)

func TestSignify(t *testing.T) {
tmpFile, err := ioutil.TempFile("", "")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmpFile.Name())
defer tmpFile.Close()

rand.Seed(time.Now().UnixNano())

data := make([]byte, 1024)
rand.Read(data)
tmpFile.Write(data)

if err = tmpFile.Close(); err != nil {
t.Fatal(err)
}

err = SignifySignFile(tmpFile.Name(), fmt.Sprintf("%s.sig", tmpFile.Name()), testSecKey)
if err != nil {
t.Fatal(err)
}
defer os.Remove(fmt.Sprintf("%s.sig", tmpFile.Name()))

// if signify-openbsd is present, check the signature.
// signify-openbsd will be present in CI.
if runtime.GOOS == "linux" {
cmd := exec.Command("which", "signify-openbsd")
if err = cmd.Run(); err == nil {
// Write the public key into the file to pass it as
// an argument to signify-openbsd
pubKeyFile, err := ioutil.TempFile("", "")
if err != nil {
t.Fatal(err)
}
defer os.Remove(pubKeyFile.Name())
defer pubKeyFile.Close()
pubKeyFile.WriteString("untrusted comment: signify public key\n")
pubKeyFile.WriteString(testPubKey)
pubKeyFile.WriteString("\n")

cmd := exec.Command("signify-openbsd", "-V", "-p", pubKeyFile.Name(), "-x", fmt.Sprintf("%s.sig", tmpFile.Name()), "-m", tmpFile.Name())
if output, err := cmd.CombinedOutput(); err != nil {
fmt.Println(string(output))
t.Fatalf("could not verify the file: %v", err)
}
}
}
}