shared: Add file verification
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
This commit is contained in:
parent
b16fb338d8
commit
0460f8867c
@ -82,19 +82,35 @@ func Download(file, checksum string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// downloadChecksum downloads or opens URL, and matches fname against the
|
||||
// sha256sums inside of the downloaded or opened file.
|
||||
func downloadChecksum(URL string, fname string) (string, error) {
|
||||
var client http.Client
|
||||
var (
|
||||
client http.Client
|
||||
tempFile *os.File
|
||||
err error
|
||||
)
|
||||
|
||||
tempFile, err := ioutil.TempFile(os.TempDir(), "sha256.")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer os.Remove(tempFile.Name())
|
||||
// do not re-download checksum file if it's already present
|
||||
fi, err := os.Stat(filepath.Join(os.TempDir(), URL))
|
||||
if err == nil && !fi.IsDir() {
|
||||
tempFile, err = os.Open(filepath.Join(os.TempDir(), URL))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer os.Remove(tempFile.Name())
|
||||
} else {
|
||||
tempFile, err = ioutil.TempFile(os.TempDir(), "sha256.")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer os.Remove(tempFile.Name())
|
||||
|
||||
_, err = lxd.DownloadFileSha256(&client, "", nil, nil, "", URL, "", tempFile)
|
||||
// ignore hash mismatch
|
||||
if err != nil && !strings.HasPrefix(err.Error(), "Hash mismatch") {
|
||||
return "", err
|
||||
_, err = lxd.DownloadFileSha256(&client, "", nil, nil, "", URL, "", tempFile)
|
||||
// ignore hash mismatch
|
||||
if err != nil && !strings.HasPrefix(err.Error(), "Hash mismatch") {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
tempFile.Seek(0, 0)
|
||||
@ -102,7 +118,6 @@ func downloadChecksum(URL string, fname string) (string, error) {
|
||||
scanner := bufio.NewScanner(tempFile)
|
||||
for scanner.Scan() {
|
||||
s := strings.Split(scanner.Text(), " ")
|
||||
//if len(s) == 2 && s[1] == fmt.Sprintf("*%s", filepath.Base(fname)) {
|
||||
matched, _ := regexp.MatchString(fmt.Sprintf(".*%s", filepath.Base(fname)), s[len(s)-1])
|
||||
if matched {
|
||||
return s[0], nil
|
||||
|
@ -1,9 +1,13 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
lxd "github.com/lxc/lxd/shared"
|
||||
)
|
||||
|
||||
// Copy copies a file.
|
||||
@ -42,3 +46,35 @@ func RunCommand(name string, arg ...string) error {
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// VerifyFile verifies a file using gpg.
|
||||
func VerifyFile(signedFile, signatureFile string, keys []string) (bool, error) {
|
||||
var out string
|
||||
|
||||
gpgDir := filepath.Join(os.TempDir(), "distrobuilder.gpg")
|
||||
|
||||
err := os.MkdirAll(gpgDir, 0700)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer os.RemoveAll(gpgDir)
|
||||
|
||||
out, err = lxd.RunCommand("gpg", append([]string{"--homedir", gpgDir, "--recv-keys"}, keys...)...)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Failed to receive keys: %s", out)
|
||||
}
|
||||
|
||||
if signatureFile != "" {
|
||||
out, err = lxd.RunCommand("gpg", "--homedir", gpgDir, "--verify", signatureFile, signedFile)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Failed to verify: %s", out)
|
||||
}
|
||||
} else {
|
||||
out, err = lxd.RunCommand("gpg", "--homedir", gpgDir, "--verify", signedFile)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Failed to verify: %s", out)
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
81
shared/util_test.go
Normal file
81
shared/util_test.go
Normal file
@ -0,0 +1,81 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestVerifyFile(t *testing.T) {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
fmt.Errorf("Failed to retrieve working directory: %s", err)
|
||||
}
|
||||
testdataDir := filepath.Join(wd, "..", "testdata")
|
||||
|
||||
keys := []string{"0x5DE8949A899C8D99"}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
signedFile string
|
||||
signatureFile string
|
||||
keys []string
|
||||
shouldFail bool
|
||||
}{
|
||||
{
|
||||
"testfile with detached signature",
|
||||
filepath.Join(testdataDir, "testfile"),
|
||||
filepath.Join(testdataDir, "testfile.sig"),
|
||||
keys,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"testfile with cleartext signature",
|
||||
filepath.Join(testdataDir, "testfile.asc"),
|
||||
"",
|
||||
keys,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"testfile with invalid cleartext signature",
|
||||
filepath.Join(testdataDir, "testfile-invalid.asc"),
|
||||
"",
|
||||
keys,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"testfile with normal signature",
|
||||
filepath.Join(testdataDir, "testfile.gpg"),
|
||||
"",
|
||||
keys,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"no keys",
|
||||
filepath.Join(testdataDir, "testfile"),
|
||||
filepath.Join(testdataDir, "testfile.sig"),
|
||||
[]string{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"invalid key",
|
||||
filepath.Join(testdataDir, "testfile.asc"),
|
||||
"",
|
||||
[]string{"0x46181433FBB75451"},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
log.Printf("Running test #%d: %s", i, tt.name)
|
||||
valid, err := VerifyFile(tt.signedFile, tt.signatureFile, tt.keys)
|
||||
if !tt.shouldFail && !valid {
|
||||
t.Fatalf("Failed to verify: %s\n%s", tt.name, err)
|
||||
}
|
||||
if tt.shouldFail && valid {
|
||||
t.Fatalf("Expected to fail: %s", tt.name)
|
||||
}
|
||||
}
|
||||
}
|
1
testdata/testfile
vendored
Normal file
1
testdata/testfile
vendored
Normal file
@ -0,0 +1 @@
|
||||
I need to be verified.
|
20
testdata/testfile-invalid.asc
vendored
Normal file
20
testdata/testfile-invalid.asc
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
-----BEGIN PGP SIGNED MESSAGE-----
|
||||
Hash: SHA512
|
||||
|
||||
I need to be verified.
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
kQIzBAEBCgAdFiEEYfHyDZhnHyXKGYh8mTQI0RN7fVEFAlqFpu8ACgkQmTQI0RN7
|
||||
fVGj3w/7BCzAkG995rA/7ba371SW/5uifLKxEn/izWzuJsEO40BN0rzV53XsIqew
|
||||
TMhudZo2r1lF7L0KkVChCl/E//aGB5srHRmQlogJqjdyw4qCuVmTe/QMadjo67fS
|
||||
wSqH40p5KCQeLZ33xF60vbMwf7ZwtSesFnCsQyvhu85+FDpuexGKKKDxSmO4WjHV
|
||||
lL5nDZ0vtSghw3yobGWiYBQ/6MqGLkL6yK0LAY50slywTgAb5WtSE2YCTTLeJOEi
|
||||
PEWMWbWoRYmN9ijUowo9YP6cKj4Fz0LtbWMBuHDgvO7Zl/qrb57NxRgBM0cCzAnR
|
||||
zEwjRjcfK7GGk+NyfAGbeabgJT/ATI/51sB3MBJgbd+FcSt4zMUL2qfwFDtrTqK3
|
||||
7NaKOUh7fVnsGeKY/4DSz0+hJy4qR9JuawDCuiS8CJHzp9LKxKmQDhFfpmFYWOOr
|
||||
Nqc4PifAc0OQ3n1iJGMZ0I5CSP79hRLu7FTyOEhARAz1VMR9lOmEAT+M7RcbENs6
|
||||
U06mI5h5tyKyBt0cUKQSKtYGKydR2+ZGVkkjEpodcU9RpRzvBFQMU23XdtVPNnya
|
||||
sf3ddNIbkaWkF17oxy7PW4ZFnWbA8wATEnnWi3dPIGhRRdS2qJioXFziW/idSkUB
|
||||
AagCicMVQ1XDX/Hg5HrwUBrGBk1JZ3TTwzZ/kpePgry1XSLuGxI=
|
||||
=vSiP
|
||||
-----END PGP SIGNATURE-----
|
20
testdata/testfile.asc
vendored
Normal file
20
testdata/testfile.asc
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
-----BEGIN PGP SIGNED MESSAGE-----
|
||||
Hash: SHA512
|
||||
|
||||
I need to be verified.
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iQIzBAEBCgAdFiEEYfHyDZhnHyXKGYh8mTQI0RN7fVEFAlqFpu8ACgkQmTQI0RN7
|
||||
fVGj3w/7BCzAkG995rA/7ba371SW/5uifLKxEn/izWzuJsEO40BN0rzV53XsIqew
|
||||
TMhudZo2r1lF7L0KkVChCl/E//aGB5srHRmQlogJqjdyw4qCuVmTe/QMadjo67fS
|
||||
wSqH40p5KCQeLZ33xF60vbMwf7ZwtSesFnCsQyvhu85+FDpuexGKKKDxSmO4WjHV
|
||||
lL5nDZ0vtSghw3yobGWiYBQ/6MqGLkL6yK0LAY50slywTgAb5WtSE2YCTTLeJOEi
|
||||
PEWMWbWoRYmN9ijUowo9YP6cKj4Fz0LtbWMBuHDgvO7Zl/qrb57NxRgBM0cCzAnR
|
||||
zEwjRjcfK7GGk+NyfAGbeabgJT/ATI/51sB3MBJgbd+FcSt4zMUL2qfwFDtrTqK3
|
||||
7NaKOUh7fVnsGeKY/4DSz0+hJy4qR9JuawDCuiS8CJHzp9LKxKmQDhFfpmFYWOOr
|
||||
Nqc4PifAc0OQ3n1iJGMZ0I5CSP79hRLu7FTyOEhARAz1VMR9lOmEAT+M7RcbENs6
|
||||
U06mI5h5tyKyBt0cUKQSKtYGKydR2+ZGVkkjEpodcU9RpRzvBFQMU23XdtVPNnya
|
||||
sf3ddNIbkaWkF17oxy7PW4ZFnWbA8wATEnnWi3dPIGhRRdS2qJioXFziW/idSkUB
|
||||
AagCicMVQ1XDX/Hg5HrwUBrGBk1JZ3TTwzZ/kpePgry1XSLuGxI=
|
||||
=vSiP
|
||||
-----END PGP SIGNATURE-----
|
BIN
testdata/testfile.gpg
vendored
Normal file
BIN
testdata/testfile.gpg
vendored
Normal file
Binary file not shown.
BIN
testdata/testfile.sig
vendored
Normal file
BIN
testdata/testfile.sig
vendored
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user