diff --git a/internal/arduino/resources/resources_test.go b/internal/arduino/resources/resources_test.go index 0ca1bdd884b..3cd1d7a0098 100644 --- a/internal/arduino/resources/resources_test.go +++ b/internal/arduino/resources/resources_test.go @@ -131,29 +131,45 @@ func TestIndexDownloadAndSignatureWithinArchive(t *testing.T) { require.NoError(t, err) defer ln.Close() go server.Serve(ln) + defer server.Close() - validIdxURL, err := url.Parse("http://" + ln.Addr().String() + "/valid/package_index.tar.bz2") - require.NoError(t, err) - idxResource := &IndexResource{URL: validIdxURL} - destDir, err := paths.MkTempDir("", "") - require.NoError(t, err) - defer destDir.RemoveAll() - err = idxResource.Download(ctx, destDir, func(curr *rpc.DownloadProgress) {}, downloader.GetDefaultConfig()) - require.NoError(t, err) - require.True(t, destDir.Join("package_index.json").Exist()) - require.True(t, destDir.Join("package_index.json.sig").Exist()) - - invalidIdxURL, err := url.Parse("http://" + ln.Addr().String() + "/invalid/package_index.tar.bz2") - require.NoError(t, err) - invIdxResource := &IndexResource{URL: invalidIdxURL} - invDestDir, err := paths.MkTempDir("", "") - require.NoError(t, err) - defer invDestDir.RemoveAll() - err = invIdxResource.Download(ctx, invDestDir, func(curr *rpc.DownloadProgress) {}, downloader.GetDefaultConfig()) - require.Error(t, err) - require.Contains(t, err.Error(), "invalid signature") - require.False(t, invDestDir.Join("package_index.json").Exist()) - require.False(t, invDestDir.Join("package_index.json.sig").Exist()) + { + validIdxURL, err := url.Parse("http://" + ln.Addr().String() + "/valid_signature_in_the_future/package_index.tar.bz2") + require.NoError(t, err) + idxResource := &IndexResource{URL: validIdxURL} + destDir, err := paths.MkTempDir("", "") + require.NoError(t, err) + defer destDir.RemoveAll() + err = idxResource.Download(ctx, destDir, func(curr *rpc.DownloadProgress) {}, downloader.GetDefaultConfig()) + require.ErrorContains(t, err, "is your system clock set correctly?") + require.False(t, destDir.Join("package_index.json").Exist()) + require.False(t, destDir.Join("package_index.json.sig").Exist()) + } + { + validIdxURL, err := url.Parse("http://" + ln.Addr().String() + "/valid/package_index.tar.bz2") + require.NoError(t, err) + idxResource := &IndexResource{URL: validIdxURL} + destDir, err := paths.MkTempDir("", "") + require.NoError(t, err) + defer destDir.RemoveAll() + err = idxResource.Download(ctx, destDir, func(curr *rpc.DownloadProgress) {}, downloader.GetDefaultConfig()) + require.NoError(t, err) + require.True(t, destDir.Join("package_index.json").Exist()) + require.True(t, destDir.Join("package_index.json.sig").Exist()) + } + { + invalidIdxURL, err := url.Parse("http://" + ln.Addr().String() + "/invalid/package_index.tar.bz2") + require.NoError(t, err) + invIdxResource := &IndexResource{URL: invalidIdxURL} + invDestDir, err := paths.MkTempDir("", "") + require.NoError(t, err) + defer invDestDir.RemoveAll() + err = invIdxResource.Download(ctx, invDestDir, func(curr *rpc.DownloadProgress) {}, downloader.GetDefaultConfig()) + require.Error(t, err) + require.Contains(t, err.Error(), "invalid signature") + require.False(t, invDestDir.Join("package_index.json").Exist()) + require.False(t, invDestDir.Join("package_index.json.sig").Exist()) + } } func TestIndexFileName(t *testing.T) { diff --git a/internal/arduino/resources/testdata/valid_signature_in_the_future/package_index.tar.bz2 b/internal/arduino/resources/testdata/valid_signature_in_the_future/package_index.tar.bz2 new file mode 100644 index 00000000000..22284c67be4 Binary files /dev/null and b/internal/arduino/resources/testdata/valid_signature_in_the_future/package_index.tar.bz2 differ diff --git a/internal/arduino/security/signatures.go b/internal/arduino/security/signatures.go index fb6ed9b0697..ebd86e4a037 100644 --- a/internal/arduino/security/signatures.go +++ b/internal/arduino/security/signatures.go @@ -16,12 +16,14 @@ package security import ( + "bytes" "embed" "errors" "io" "os" "github.com/ProtonMail/go-crypto/openpgp" + pgperrors "github.com/ProtonMail/go-crypto/openpgp/errors" "github.com/arduino/arduino-cli/internal/i18n" "github.com/arduino/go-paths-helper" ) @@ -71,16 +73,19 @@ func VerifySignature(targetPath *paths.Path, signaturePath *paths.Path, arduinoK if err != nil { return false, nil, errors.New(i18n.Tr("retrieving Arduino public keys: %s", err)) } - target, err := targetPath.Open() + target, err := targetPath.ReadFile() if err != nil { return false, nil, errors.New(i18n.Tr("opening target file: %s", err)) } - defer target.Close() - signature, err := signaturePath.Open() + signature, err := signaturePath.ReadFile() if err != nil { return false, nil, errors.New(i18n.Tr("opening signature file: %s", err)) } - defer signature.Close() - signer, err := openpgp.CheckDetachedSignature(keyRing, target, signature, nil) + signer, err := openpgp.CheckDetachedSignature(keyRing, bytes.NewBuffer(target), bytes.NewBuffer(signature), nil) + + if errors.Is(err, pgperrors.ErrSignatureExpired) { + err = errors.New(i18n.Tr("signature expired: is your system clock set correctly?")) + } + return (signer != nil && err == nil), signer, err }