Regres: Add llvm.Version.Download() to download and verify a LLVM toolchain

Will be used to have regres switch to LLVM 10.

Change-Id: Ia425e5d357f4877c5608b9afc89d659ebb38f701
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/43110
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
diff --git a/tests/regres/llvm/10.0.0-darwin.sig b/tests/regres/llvm/10.0.0-darwin.sig
new file mode 100644
index 0000000..6b3d049
--- /dev/null
+++ b/tests/regres/llvm/10.0.0-darwin.sig
Binary files differ
diff --git a/tests/regres/llvm/10.0.0-ubuntu.sig b/tests/regres/llvm/10.0.0-ubuntu.sig
new file mode 100644
index 0000000..5e8133b
--- /dev/null
+++ b/tests/regres/llvm/10.0.0-ubuntu.sig
Binary files differ
diff --git a/tests/regres/llvm/10.0.0-win64.sig b/tests/regres/llvm/10.0.0-win64.sig
new file mode 100644
index 0000000..2515509
--- /dev/null
+++ b/tests/regres/llvm/10.0.0-win64.sig
Binary files differ
diff --git a/tests/regres/llvm/10.0.0.pub.key b/tests/regres/llvm/10.0.0.pub.key
new file mode 100644
index 0000000..4b3cdde
--- /dev/null
+++ b/tests/regres/llvm/10.0.0.pub.key
@@ -0,0 +1,52 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+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
+67H2IH//2sf8dw==
+=ADVe
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/tests/regres/llvm/llvm.go b/tests/regres/llvm/llvm.go
index f534295..8981277 100644
--- a/tests/regres/llvm/llvm.go
+++ b/tests/regres/llvm/llvm.go
@@ -17,7 +17,11 @@
 package llvm
 
 import (
+	"bytes"
 	"fmt"
+	"io/ioutil"
+	"net/http"
+	"os"
 	"os/exec"
 	"path/filepath"
 	"regexp"
@@ -52,6 +56,79 @@
 	return v.Point >= rhs.Point
 }
 
+// Download downloads and verifies the LLVM toolchain for the current OS.
+func (v Version) Download() ([]byte, error) {
+	return v.DownloadForOS(runtime.GOOS)
+}
+
+// DownloadForOS downloads and verifies the LLVM toolchain for the given OS.
+func (v Version) DownloadForOS(osName string) ([]byte, error) {
+	url, sig, key, err := v.DownloadInfoForOS(osName)
+	if err != nil {
+		return nil, err
+	}
+
+	resp, err := http.Get(url)
+	if err != nil {
+		return nil, fmt.Errorf("Could not download LLVM from %v: %v", url, err)
+	}
+	defer resp.Body.Close()
+
+	content, err := ioutil.ReadAll(resp.Body)
+	if err != nil {
+		return nil, fmt.Errorf("Could not download LLVM from %v: %v", url, err)
+	}
+
+	sigfile, err := os.Open(sig)
+	if err != nil {
+		return nil, fmt.Errorf("Couldn't open file '%s': %v", sig, err)
+	}
+	defer sigfile.Close()
+
+	keyfile, err := os.Open(key)
+	if err != nil {
+		return nil, fmt.Errorf("Couldn't open file '%s': %v", key, err)
+	}
+	defer keyfile.Close()
+
+	if err := util.CheckPGP(bytes.NewReader(content), sigfile, keyfile); err != nil {
+		return nil, err
+	}
+	return content, nil
+}
+
+// DownloadInfoForOS returns the download url, signature and key for the given
+// LLVM version for the given OS.
+func (v Version) DownloadInfoForOS(os string) (url, sig, key string, err error) {
+	switch v {
+	case Version{10, 0, 0}:
+		key = relfile("10.0.0.pub.key")
+		switch os {
+		case "linux":
+			url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/clang+llvm-10.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz"
+			sig = relfile("10.0.0-ubuntu.sig")
+			return
+		case "darwin":
+			url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/clang+llvm-10.0.0-x86_64-apple-darwin.tar.xz"
+			sig = relfile("10.0.0-darwin.sig")
+			return
+		case "windows":
+			url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/LLVM-10.0.0-win64.exe"
+			sig = relfile("10.0.0-win64.sig")
+			return
+		default:
+			return "", "", "", fmt.Errorf("Unsupported OS: %v", os)
+		}
+	default:
+		return "", "", "", fmt.Errorf("Unknown download for LLVM %v", v)
+	}
+}
+func relfile(path string) string {
+	_, thisFile, _, _ := runtime.Caller(1)
+	thisDir := filepath.Dir(thisFile)
+	return filepath.Join(thisDir, path)
+}
+
 // Toolchain holds the paths and version information about an LLVM toolchain.
 type Toolchain struct {
 	Version Version
diff --git a/tests/regres/llvm/llvm_test.go b/tests/regres/llvm/llvm_test.go
new file mode 100644
index 0000000..d3b94fc
--- /dev/null
+++ b/tests/regres/llvm/llvm_test.go
@@ -0,0 +1,40 @@
+// Copyright 2020 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package llvm_test
+
+import (
+	"testing"
+
+	llvm "."
+)
+
+func TestLLVMDownloads(t *testing.T) {
+	for _, version := range []llvm.Version{
+		llvm.Version{Major: 10, Minor: 0, Point: 0},
+	} {
+		t.Logf("Downloading %v...", version)
+		for _, os := range []string{"linux", "darwin", "windows"} {
+			data, err := version.DownloadForOS(os)
+			switch {
+			case err != nil:
+				t.Errorf("Download of LLVM %v failed with: %v", version, err)
+			case len(data) == 0:
+				t.Errorf("Download of LLVM %v resulted in no data", version)
+			default:
+				t.Logf("done\n")
+			}
+		}
+	}
+}
diff --git a/tests/regres/util/pgp.go b/tests/regres/util/pgp.go
new file mode 100644
index 0000000..2ffccdd
--- /dev/null
+++ b/tests/regres/util/pgp.go
@@ -0,0 +1,37 @@
+// Copyright 2020 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package util
+
+import (
+	"fmt"
+	"io"
+
+	"golang.org/x/crypto/openpgp"
+)
+
+// CheckPGP verifies that data has the PGP signature sig and was signed with the
+// PGP key key.
+func CheckPGP(data, sig, key io.Reader) error {
+	keyring, err := openpgp.ReadArmoredKeyRing(key)
+	if err != nil {
+		return fmt.Errorf("Failed to read pgp key: %v", err)
+	}
+
+	if _, err := openpgp.CheckDetachedSignature(keyring, data, sig); err != nil {
+		return fmt.Errorf("Failed to verify pgp: %v", err)
+	}
+
+	return nil
+}