mirror of
https://github.com/rclone/rclone.git
synced 2026-11-07 10:12:56 -05:00
rclone selfupdate checked the PGP signature on the clearsigned block of SHA256SUMS but then looked up the archive hash in the whole downloaded file. Unsigned data can appear in that file without invalidating the signature. A hash line placed before or after the signed text was accepted, allowing whoever controlled the download to install an arbitrary unsigned archive. The hash is now only looked up in the signed text. Affects stable (non --beta) selfupdate since v1.55.0. --beta updates are not signature checked.
62 lines
2.2 KiB
Go
62 lines
2.2 KiB
Go
//go:build !noselfupdate
|
|
|
|
package selfupdate
|
|
|
|
import (
|
|
"context"
|
|
"encoding/hex"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestVerify(t *testing.T) {
|
|
ctx := context.Background()
|
|
sumsBuf, err := os.ReadFile("testdata/verify/SHA256SUMS")
|
|
require.NoError(t, err)
|
|
hash, err := hex.DecodeString("b20b47f579a2c790ca752fb5d8e5651fade7d5867cbac0a4f71e805fc5c468d0")
|
|
require.NoError(t, err)
|
|
|
|
t.Run("NoError", func(t *testing.T) {
|
|
err = verifyHashsumDownloaded(ctx, sumsBuf, "archive.zip", hash)
|
|
require.NoError(t, err)
|
|
})
|
|
t.Run("BadSig", func(t *testing.T) {
|
|
sumsBuf[0x60] ^= 1 // change the signature by one bit
|
|
err = verifyHashsumDownloaded(ctx, sumsBuf, "archive.zip", hash)
|
|
assert.ErrorContains(t, err, "invalid signature")
|
|
sumsBuf[0x60] ^= 1 // undo the change
|
|
})
|
|
t.Run("BadSum", func(t *testing.T) {
|
|
hash[0] ^= 1 // change the SHA256 by one bit
|
|
err = verifyHashsumDownloaded(ctx, sumsBuf, "archive.zip", hash)
|
|
assert.ErrorContains(t, err, "archive hash mismatch")
|
|
hash[0] ^= 1 // undo the change
|
|
})
|
|
t.Run("BadName", func(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)
|
|
})
|
|
}
|