diff --git a/cmd/selfupdate/verify.go b/cmd/selfupdate/verify.go index 78ba46f3b..635f39e69 100644 --- a/cmd/selfupdate/verify.go +++ b/cmd/selfupdate/verify.go @@ -86,7 +86,8 @@ func verifyHashsumDownloaded(ctx context.Context, sumsBuf []byte, archive string return fmt.Errorf("invalid hashsum signature: %w", err) } - wantHash, err := findFileHash(sumsBuf, archive) + // Only read hashes from the the signed text + wantHash, err := findFileHash(block.Bytes, archive) if err != nil { return err } diff --git a/cmd/selfupdate/verify_test.go b/cmd/selfupdate/verify_test.go index 2c8519f40..20ef34487 100644 --- a/cmd/selfupdate/verify_test.go +++ b/cmd/selfupdate/verify_test.go @@ -6,6 +6,7 @@ import ( "context" "encoding/hex" "os" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -39,4 +40,22 @@ func TestVerify(t *testing.T) { err = verifyHashsumDownloaded(ctx, sumsBuf, "archive.zipX", hash) assert.ErrorContains(t, err, "unable to find hash") }) + t.Run("UnsignedPrefix", func(t *testing.T) { + hash[0] ^= 1 // forge an unsigned hash line before the signed block + prefixed := append([]byte(hex.EncodeToString(hash)+" archive.zip\n"), sumsBuf...) + err = verifyHashsumDownloaded(ctx, prefixed, "archive.zip", hash) + assert.ErrorContains(t, err, "archive hash mismatch") + hash[0] ^= 1 // undo the change + }) + t.Run("UnsignedInArmor", func(t *testing.T) { + // Dash escaping the signed line doesn't change the signed text but hides + // it from a raw scan, and lines after the armor checksum are ignored. + badHash := hex.EncodeToString([]byte{0x01}) + armored := strings.Replace(string(sumsBuf), "b20b", "- b20b", 1) + armored = strings.Replace(armored, "=1GTr\n", "=1GTr\n"+badHash+" archive.zip\n", 1) + err = verifyHashsumDownloaded(ctx, []byte(armored), "archive.zip", []byte{0x01}) + assert.ErrorContains(t, err, "archive hash mismatch") + err = verifyHashsumDownloaded(ctx, []byte(armored), "archive.zip", hash) + require.NoError(t, err) + }) }