|
|
|
// Copyright 2020 The go-ethereum Authors
|
|
|
|
// This file is part of go-ethereum.
|
|
|
|
//
|
|
|
|
// go-ethereum is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// go-ethereum 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 General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"regexp"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/jedisct1/go-minisign"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestVerification(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
// Signatures generated with `minisign`. Legacy format, not pre-hashed file.
|
|
|
|
t.Run("minisig-legacy", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
// For this test, the pubkey is in testdata/vcheck/minisign.pub
|
|
|
|
// (the privkey is `minisign.sec`, if we want to expand this test. Password 'test' )
|
|
|
|
pub := "RWQkliYstQBOKOdtClfgC3IypIPX6TAmoEi7beZ4gyR3wsaezvqOMWsp"
|
|
|
|
testVerification(t, pub, "./testdata/vcheck/minisig-sigs/")
|
|
|
|
})
|
|
|
|
t.Run("minisig-new", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
// For this test, the pubkey is in testdata/vcheck/minisign.pub
|
|
|
|
// (the privkey is `minisign.sec`, if we want to expand this test. Password 'test' )
|
|
|
|
// `minisign -S -s ./minisign.sec -m data.json -x ./minisig-sigs-new/data.json.minisig`
|
|
|
|
pub := "RWQkliYstQBOKOdtClfgC3IypIPX6TAmoEi7beZ4gyR3wsaezvqOMWsp"
|
|
|
|
testVerification(t, pub, "./testdata/vcheck/minisig-sigs-new/")
|
|
|
|
})
|
|
|
|
// Signatures generated with `signify-openbsd`
|
|
|
|
t.Run("signify-openbsd", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
t.Skip("This currently fails, minisign expects 4 lines of data, signify provides only 2")
|
|
|
|
// For this test, the pubkey is in testdata/vcheck/signifykey.pub
|
|
|
|
// (the privkey is `signifykey.sec`, if we want to expand this test. Password 'test' )
|
|
|
|
pub := "RWSKLNhZb0KdATtRT7mZC/bybI3t3+Hv/O2i3ye04Dq9fnT9slpZ1a2/"
|
|
|
|
testVerification(t, pub, "./testdata/vcheck/signify-sigs/")
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func testVerification(t *testing.T, pubkey, sigdir string) {
|
|
|
|
// Data to verify
|
|
|
|
data, err := os.ReadFile("./testdata/vcheck/data.json")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
// Signatures, with and without comments, both trusted and untrusted
|
|
|
|
files, err := os.ReadDir(sigdir)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if len(files) == 0 {
|
|
|
|
t.Fatal("Missing tests")
|
|
|
|
}
|
|
|
|
for _, f := range files {
|
|
|
|
sig, err := os.ReadFile(filepath.Join(sigdir, f.Name()))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
err = verifySignature([]string{pubkey}, data, sig)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func versionUint(v string) int {
|
|
|
|
mustInt := func(s string) int {
|
|
|
|
a, err := strconv.Atoi(s)
|
|
|
|
if err != nil {
|
|
|
|
panic(v)
|
|
|
|
}
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
components := strings.Split(strings.TrimPrefix(v, "v"), ".")
|
|
|
|
a := mustInt(components[0])
|
|
|
|
b := mustInt(components[1])
|
|
|
|
c := mustInt(components[2])
|
|
|
|
return a*100*100 + b*100 + c
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestMatching can be used to check that the regexps are correct
|
|
|
|
func TestMatching(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
data, _ := os.ReadFile("./testdata/vcheck/vulnerabilities.json")
|
|
|
|
var vulns []vulnJson
|
|
|
|
if err := json.Unmarshal(data, &vulns); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
check := func(version string) {
|
|
|
|
vFull := fmt.Sprintf("Geth/%v-unstable-15339cf1-20201204/linux-amd64/go1.15.4", version)
|
|
|
|
for _, vuln := range vulns {
|
|
|
|
r, err := regexp.Compile(vuln.Check)
|
|
|
|
vulnIntro := versionUint(vuln.Introduced)
|
|
|
|
vulnFixed := versionUint(vuln.Fixed)
|
|
|
|
current := versionUint(version)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if vuln.Name == "Denial of service due to Go CVE-2020-28362" {
|
|
|
|
// this one is not tied to geth-versions
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if vulnIntro <= current && vulnFixed > current {
|
|
|
|
// Should be vulnerable
|
|
|
|
if !r.MatchString(vFull) {
|
|
|
|
t.Errorf("Should be vulnerable, version %v, intro: %v, fixed: %v %v %v",
|
|
|
|
version, vuln.Introduced, vuln.Fixed, vuln.Name, vuln.Check)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if r.MatchString(vFull) {
|
|
|
|
t.Errorf("Should not be flagged vulnerable, version %v, intro: %v, fixed: %v %v %d %d %d",
|
|
|
|
version, vuln.Introduced, vuln.Fixed, vuln.Name, vulnIntro, current, vulnFixed)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for major := 1; major < 2; major++ {
|
|
|
|
for minor := 0; minor < 30; minor++ {
|
|
|
|
for patch := 0; patch < 30; patch++ {
|
|
|
|
vShort := fmt.Sprintf("v%d.%d.%d", major, minor, patch)
|
|
|
|
check(vShort)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGethPubKeysParseable(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
for _, pubkey := range gethPubKeys {
|
|
|
|
_, err := minisign.NewPublicKey(pubkey)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Should be parseable")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestKeyID(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
type args struct {
|
|
|
|
id [8]byte
|
|
|
|
}
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
args args
|
|
|
|
want string
|
|
|
|
}{
|
|
|
|
{"@holiman key", args{id: extractKeyId(gethPubKeys[0])}, "FB1D084D39BAEC24"},
|
|
|
|
{"second key", args{id: extractKeyId(gethPubKeys[1])}, "138B1CA303E51687"},
|
|
|
|
{"third key", args{id: extractKeyId(gethPubKeys[2])}, "FD9813B2D2098484"},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
tt := tt
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
if got := keyID(tt.args.id); got != tt.want {
|
|
|
|
t.Errorf("keyID() = %v, want %v", got, tt.want)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func extractKeyId(pubkey string) [8]byte {
|
|
|
|
p, _ := minisign.NewPublicKey(pubkey)
|
|
|
|
return p.KeyId
|
|
|
|
}
|