go-pulse/crypto/signify/signify.go
Guillaume Ballet fa572cd297
crypto: signing builds with signify/minisign (#21798)
* internal/build: implement signify's signing func
* Add signify to the ci utility
* fix output file format
* Add unit test for signify
* holiman's + travis' feedback
* internal/build: verify signify's output
* crypto: move signify to common dir
* use go-minisign to verify binaries
* more holiman feedback
* crypto, ci: support minisign output
* only accept one-line trusted comments
* configurable untrusted comments
* code cleanup in tests
* revert to use ed25519 from the stdlib
* bug: fix for empty untrusted comments
* write timestamp as comment if trusted comment isn't present
* rename line checker to commentHasManyLines
* crypto: added signify fuzzer (#6)
* crypto: added signify fuzzer
* stuff
* crypto: updated signify fuzzer to fuzz comments
* crypto: repro signify crashes
* rebased fuzzer on build-signify branch
* hide fuzzer behind gofuzz build flag
* extract key data inside a single function
* don't treat \r as a newline
* travis: fix signing command line
* do not use an external binary in tests
* crypto: move signify to crypto/signify
* travis: fix formatting issue
* ci: fix linter build after package move

Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de>
2020-11-27 12:13:54 +01:00

119 lines
3.3 KiB
Go

// 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 signify
import (
"encoding/base64"
"errors"
"fmt"
"io/ioutil"
"os"
"strings"
"time"
"crypto/ed25519"
)
var (
errInvalidKeyHeader = errors.New("Incorrect key header")
errInvalidKeyLength = errors.New("invalid, key length != 104")
)
func parsePrivateKey(key string) (ed25519.PrivateKey, []byte, []byte, 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
}
func commentHasManyLines(comment string) bool {
firstLFIndex := strings.IndexByte(comment, 10)
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 {
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()
skey, header, keyNum, err := parsePrivateKey(key)
if err != nil {
return err
}
filedata, err := ioutil.ReadAll(in)
if err != nil {
return err
}
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
}