From 4f0534a3cffe439a4f51356e8d4478c913816aa0 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 8 Dec 2020 23:36:08 +0100 Subject: [PATCH 1/6] crypto/signify: rename SignifySignFile -> SignFile and improve docs --- crypto/signify/signify.go | 7 +++++-- crypto/signify/signify_test.go | 8 ++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/crypto/signify/signify.go b/crypto/signify/signify.go index e86c4f09b0bd..31995dafd88d 100644 --- a/crypto/signify/signify.go +++ b/crypto/signify/signify.go @@ -58,8 +58,11 @@ func commentHasManyLines(comment string) bool { return (firstLFIndex >= 0 && firstLFIndex < len(comment)-1) } -// SignifySignFile creates a signature of the input file. -func SignifySignFile(input string, output string, key string, unTrustedComment string, trustedComment string) error { +// SignFile creates a signature of the input file. +// +// This accepts base64 keys in the format created by the 'signify' tool. +// The signature is written to the 'output' file. +func SignFile(input string, output string, key string, unTrustedComment string, trustedComment string) error { in, err := os.Open(input) if err != nil { return err diff --git a/crypto/signify/signify_test.go b/crypto/signify/signify_test.go index af77eaf227e9..615d4e652792 100644 --- a/crypto/signify/signify_test.go +++ b/crypto/signify/signify_test.go @@ -52,7 +52,7 @@ func TestSignify(t *testing.T) { t.Fatal(err) } - err = SignifySignFile(tmpFile.Name(), tmpFile.Name()+".sig", testSecKey, "clé", "croissants") + err = SignFile(tmpFile.Name(), tmpFile.Name()+".sig", testSecKey, "clé", "croissants") if err != nil { t.Fatal(err) } @@ -96,7 +96,7 @@ func TestSignifyTrustedCommentTooManyLines(t *testing.T) { t.Fatal(err) } - err = SignifySignFile(tmpFile.Name(), tmpFile.Name()+".sig", testSecKey, "", "crois\nsants") + err = SignFile(tmpFile.Name(), tmpFile.Name()+".sig", testSecKey, "", "crois\nsants") if err == nil || err.Error() == "" { t.Fatalf("should have errored on a multi-line trusted comment, got %v", err) } @@ -121,7 +121,7 @@ func TestSignifyTrustedCommentTooManyLinesLF(t *testing.T) { t.Fatal(err) } - err = SignifySignFile(tmpFile.Name(), tmpFile.Name()+".sig", testSecKey, "crois\rsants", "") + err = SignFile(tmpFile.Name(), tmpFile.Name()+".sig", testSecKey, "crois\rsants", "") if err != nil { t.Fatal(err) } @@ -146,7 +146,7 @@ func TestSignifyTrustedCommentEmpty(t *testing.T) { t.Fatal(err) } - err = SignifySignFile(tmpFile.Name(), tmpFile.Name()+".sig", testSecKey, "", "") + err = SignFile(tmpFile.Name(), tmpFile.Name()+".sig", testSecKey, "", "") if err != nil { t.Fatal(err) } From beeda0f5107ba8b6df7ceda2aff7e7e9d8b25694 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 9 Dec 2020 00:23:02 +0100 Subject: [PATCH 2/6] crypto/signify: improve file handling in SignFile If there was an issue during signing, an empty or incomplete output file would be created. Fix this by using ioutil.WriteFile at the end instead of opening the output file first and writing it incrementally. --- crypto/signify/signify.go | 90 ++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 53 deletions(-) diff --git a/crypto/signify/signify.go b/crypto/signify/signify.go index 31995dafd88d..63b07a51e464 100644 --- a/crypto/signify/signify.go +++ b/crypto/signify/signify.go @@ -20,36 +20,32 @@ package signify import ( + "bytes" + "crypto/ed25519" "encoding/base64" "errors" "fmt" "io/ioutil" - "os" "strings" "time" - - "crypto/ed25519" ) var ( - errInvalidKeyHeader = errors.New("Incorrect key header") + errInvalidKeyHeader = errors.New("incorrect key header") errInvalidKeyLength = errors.New("invalid, key length != 104") ) -func parsePrivateKey(key string) (ed25519.PrivateKey, []byte, []byte, error) { +func parsePrivateKey(key string) (k ed25519.PrivateKey, header []byte, keyNum []byte, err error) { keydata, err := base64.StdEncoding.DecodeString(key) if err != nil { return nil, nil, nil, err } - if len(keydata) != 104 { return nil, nil, nil, errInvalidKeyLength } - if string(keydata[:2]) != "Ed" { return nil, nil, nil, errInvalidKeyHeader } - return ed25519.PrivateKey(keydata[40:]), keydata[:2], keydata[32:40], nil } @@ -62,60 +58,48 @@ func commentHasManyLines(comment string) bool { // // This accepts base64 keys in the format created by the 'signify' tool. // The signature is written to the 'output' file. -func SignFile(input string, output string, key string, unTrustedComment string, trustedComment string) error { - in, err := os.Open(input) - if err != nil { - return err +func SignFile(input string, output string, key string, untrustedComment string, trustedComment string) error { + // Pre-check comments and ensure they're set to something. + if commentHasManyLines(untrustedComment) { + return errors.New("untrusted comment must fit on a single line") } - defer in.Close() - - out, err := os.Create(output) - if err != nil { - return err + if commentHasManyLines(trustedComment) { + return errors.New("trusted comment must fit on a single line") + } + if untrustedComment == "" { + untrustedComment = "verify with " + input + ".pub" + } + if trustedComment == "" { + trustedComment = fmt.Sprintf("timestamp:%d", time.Now().Unix()) } - defer out.Close() - skey, header, keyNum, err := parsePrivateKey(key) + filedata, err := ioutil.ReadFile(input) if err != nil { return err } - - filedata, err := ioutil.ReadAll(in) + skey, header, keyNum, err := parsePrivateKey(key) if err != nil { return err } + // Create the main data signature. rawSig := ed25519.Sign(skey, filedata) - - var sigdata []byte - sigdata = append(sigdata, header...) - sigdata = append(sigdata, keyNum...) - sigdata = append(sigdata, rawSig...) - - // Check that the trusted comment fits in one line - if commentHasManyLines(unTrustedComment) { - return errors.New("untrusted comment must fit on a single line") - } - - if unTrustedComment == "" { - unTrustedComment = "verify with " + input + ".pub" - } - out.WriteString(fmt.Sprintf("untrusted comment: %s\n%s\n", unTrustedComment, base64.StdEncoding.EncodeToString(sigdata))) - - // Add the trusted comment if unavailable - if trustedComment == "" { - trustedComment = fmt.Sprintf("timestamp:%d", time.Now().Unix()) - } - - // Check that the trusted comment fits in one line - if commentHasManyLines(trustedComment) { - return errors.New("trusted comment must fit on a single line") - } - - var sigAndComment []byte - sigAndComment = append(sigAndComment, rawSig...) - sigAndComment = append(sigAndComment, []byte(trustedComment)...) - out.WriteString(fmt.Sprintf("trusted comment: %s\n%s\n", trustedComment, base64.StdEncoding.EncodeToString(ed25519.Sign(skey, sigAndComment)))) - - return nil + var dataSig []byte + dataSig = append(dataSig, header...) + dataSig = append(dataSig, keyNum...) + dataSig = append(dataSig, rawSig...) + + // Create the comment signature. + var commentSigInput []byte + commentSigInput = append(commentSigInput, rawSig...) + commentSigInput = append(commentSigInput, []byte(trustedComment)...) + commentSig := ed25519.Sign(skey, commentSigInput) + + // Create the output file. + var out = new(bytes.Buffer) + fmt.Fprintln(out, "untrusted comment:", untrustedComment) + fmt.Fprintln(out, base64.StdEncoding.EncodeToString(dataSig)) + fmt.Fprintln(out, "trusted comment:", trustedComment) + fmt.Fprintln(out, base64.StdEncoding.EncodeToString(commentSig)) + return ioutil.WriteFile(output, out.Bytes(), 0644) } From f28677bdd05336530233ac30cae2898921dcf691 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 9 Dec 2020 00:32:40 +0100 Subject: [PATCH 3/6] crypto/signify: be stricter about newlines in comments Since we now use Fprintln to write the comments, ensure they don't contain any newline character. --- crypto/signify/signify.go | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/crypto/signify/signify.go b/crypto/signify/signify.go index 63b07a51e464..7ba9705491cf 100644 --- a/crypto/signify/signify.go +++ b/crypto/signify/signify.go @@ -49,22 +49,17 @@ func parsePrivateKey(key string) (k ed25519.PrivateKey, header []byte, keyNum [] return ed25519.PrivateKey(keydata[40:]), keydata[:2], keydata[32:40], nil } -func commentHasManyLines(comment string) bool { - firstLFIndex := strings.IndexByte(comment, 10) - return (firstLFIndex >= 0 && firstLFIndex < len(comment)-1) -} - // SignFile creates a signature of the input file. // // This accepts base64 keys in the format created by the 'signify' tool. // The signature is written to the 'output' file. func SignFile(input string, output string, key string, untrustedComment string, trustedComment string) error { // Pre-check comments and ensure they're set to something. - if commentHasManyLines(untrustedComment) { - return errors.New("untrusted comment must fit on a single line") + if strings.IndexByte(untrustedComment, '\n') >= 0 { + return errors.New("untrusted comment must not contain newline") } - if commentHasManyLines(trustedComment) { - return errors.New("trusted comment must fit on a single line") + if strings.IndexByte(trustedComment, '\n') >= 0 { + return errors.New("trusted comment must not contain newline") } if untrustedComment == "" { untrustedComment = "verify with " + input + ".pub" From 2ec3e33b1618485c39590b1cf007adedea080744 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Tue, 8 Dec 2020 23:37:03 +0100 Subject: [PATCH 4/6] build: fix signify archive signing The archive signing step used getenvBase64, which decodes the key data as base64. This is incorrect here because crypto/signify already base64 decodes the key. --- build/ci.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/build/ci.go b/build/ci.go index 951b21f9108a..c01c74c88f09 100644 --- a/build/ci.go +++ b/build/ci.go @@ -58,7 +58,7 @@ import ( "time" "github.com/cespare/cp" - signifyPkg "github.com/ethereum/go-ethereum/crypto/signify" + "github.com/ethereum/go-ethereum/crypto/signify" "github.com/ethereum/go-ethereum/internal/build" "github.com/ethereum/go-ethereum/params" ) @@ -449,7 +449,7 @@ func archiveBasename(arch string, archiveVersion string) string { return platform + "-" + archiveVersion } -func archiveUpload(archive string, blobstore string, signer string, signify string) error { +func archiveUpload(archive string, blobstore string, signer string, signifyVar string) error { // If signing was requested, generate the signature files if signer != "" { key := getenvBase64(signer) @@ -457,9 +457,9 @@ func archiveUpload(archive string, blobstore string, signer string, signify stri return err } } - if signify != "" { - key := getenvBase64(string(signify)) - if err := signifyPkg.SignifySignFile(archive, archive+".sig", string(key), "verify with geth.pub", fmt.Sprintf("%d", time.Now().UTC().Unix())); err != nil { + if signifyVar != "" { + key := os.Getenv(signifyVar) + if err := signify.SignFile(archive, archive+".sig", key, "verify with geth.pub", fmt.Sprintf("%d", time.Now().UTC().Unix())); err != nil { return err } } @@ -478,7 +478,7 @@ func archiveUpload(archive string, blobstore string, signer string, signify stri return err } } - if signify != "" { + if signifyVar != "" { if err := build.AzureBlobstoreUpload(archive+".sig", filepath.Base(archive+".sig"), auth); err != nil { return err } From 5d0e5beb131259ac4898e3fc91898d0eb5a3325b Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 9 Dec 2020 00:07:26 +0100 Subject: [PATCH 5/6] build: improve trusted signature comment Put the archive name in there, and format the timestamp in a human-readable way. --- build/ci.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build/ci.go b/build/ci.go index c01c74c88f09..15946c257e16 100644 --- a/build/ci.go +++ b/build/ci.go @@ -459,7 +459,9 @@ func archiveUpload(archive string, blobstore string, signer string, signifyVar s } if signifyVar != "" { key := os.Getenv(signifyVar) - if err := signify.SignFile(archive, archive+".sig", key, "verify with geth.pub", fmt.Sprintf("%d", time.Now().UTC().Unix())); err != nil { + untrustedComment := "verify with geth-release.pub" + trustedComment := fmt.Sprintf("%s (%s)", archive, time.Now().UTC().Format(time.RFC1123)) + if err := signify.SignFile(archive, archive+".sig", key, untrustedComment, trustedComment); err != nil { return err } } From 9883378f372ce4cf261554552f5388420b4544db Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 9 Dec 2020 00:56:08 +0100 Subject: [PATCH 6/6] .travis.yml: use single variable for signify key --- .travis.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index d37458792a42..1268c6d65724 100644 --- a/.travis.yml +++ b/.travis.yml @@ -67,22 +67,22 @@ jobs: 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 -signify LINUX_SIGNIFY_KEY -upload gethstore/builds + - go run build/ci.go archive -type tar -signer LINUX_SIGNING_KEY -signify 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 -signify LINUX_SIGNIFY_KEY -upload gethstore/builds + - go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -signify 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 -signify LINUX_SIGNIFY_KEY -upload gethstore/builds + - GOARM=5 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds - 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 -signify LINUX_SIGNIFY_KEY -upload gethstore/builds + - GOARM=6 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -signify 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 -signify LINUX_SIGNIFY_KEY -upload gethstore/builds + - GOARM=7 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -signify 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 -signify LINUX_SIGNIFY_KEY -upload gethstore/builds + - go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds # This builder does the Linux Azure MIPS xgo uploads - stage: build @@ -100,19 +100,19 @@ jobs: script: - 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 -signify LINUX_SIGNIFY_KEY -upload gethstore/builds + - go run build/ci.go archive -arch mips -type tar -signer LINUX_SIGNING_KEY -signify 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 -signify LINUX_SIGNIFY_KEY -upload gethstore/builds + - go run build/ci.go archive -arch mipsle -type tar -signer LINUX_SIGNING_KEY -signify 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 signify LINUX_SIGNIFY_KEY -upload gethstore/builds + - go run build/ci.go archive -arch mips64 -type tar -signer LINUX_SIGNING_KEY signify 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 -signify LINUX_SIGNIFY_KEY -upload gethstore/builds + - go run build/ci.go archive -arch mips64le -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds # This builder does the Android Maven and Azure uploads - stage: build @@ -151,7 +151,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 -signify ANDROID_SIGNIFY_KEY -deploy https://oss.sonatype.org -upload gethstore/builds + - go run build/ci.go aar -signer ANDROID_SIGNING_KEY -signify SIGNIFY_KEY -deploy https://oss.sonatype.org -upload gethstore/builds # This builder does the OSX Azure, iOS CocoaPods and iOS Azure uploads - stage: build @@ -167,7 +167,7 @@ jobs: submodules: false # avoid cloning ethereum/tests script: - go run build/ci.go install -dlgo - - go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -signify OSX_SIGNIFY_KEY -upload gethstore/builds + - go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds # Build the iOS framework and upload it to CocoaPods and Azure - gem uninstall cocoapods -a -x @@ -182,7 +182,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 -signify IOS_SIGNIFY_KEY -deploy trunk -upload gethstore/builds + - go run build/ci.go xcode -signer IOS_SIGNING_KEY -signify SIGNIFY_KEY -deploy trunk -upload gethstore/builds # These builders run the tests - stage: build