Add better support for hash calculations

Now does directory and symlinks.
Appears to agree with swupd hashdump
No longer imports golang.org/x/sys/unix

Added support for the 0 value of hashval to map to the
0000000000000000000000000000000000000000000000000000000000000000
string. Updated tests. Added resetHash function to testing to enforce
this. Added setHashZero method for file to set this zero hash.

Signed-off-by: Icarus Sparry <icarus.w.sparry@intel.com>
This commit is contained in:
Icarus Sparry
2018-01-05 16:16:56 -08:00
committed by tmarcu
parent 21d6247597
commit 7be0bc2412
4 changed files with 66 additions and 28 deletions
+4
View File
@@ -180,6 +180,10 @@ func (f *File) setHash(hash string) error {
return nil
}
func (f *File) setHashZero() {
f.Hash = 0
}
func (f *File) getHashString() string {
return *Hashes[f.Hash]
}
+4 -2
View File
@@ -2,9 +2,11 @@ package swupd
type hashval int
const AllZeroHash = "0000000000000000000000000000000000000000000000000000000000000000"
// Hashes is a global map of indices to hashes
var Hashes = []*string{}
var invHash = make(map[string]hashval)
var Hashes = []*string{&AllZeroHash}
var invHash = map[string]hashval{AllZeroHash: 0}
// internHash adds only new hashes to the Hashes slice and returns the index at
// which they are located
+15 -9
View File
@@ -5,21 +5,27 @@ import (
"testing"
)
func resetHash() {
Hashes = []*string{&allzero}
invHash = map[string]hashval{allzero: 0}
}
func TestInternHash(t *testing.T) {
// reset Hashes so we get the expected indices
Hashes = []*string{}
resetHash()
testCases := []struct {
hash string
expected hashval
}{
{"9bcc1718757db298fb656ae6e2ee143dde746f49fbf6805db7683cb574c36728", 0},
{"33ccead640727d66c62be03e089a3ca3f4ef7c374a3eeab79764f9509075b0d8", 1},
{"33ccead640727d66c62be03e089a3ca3f4ef7c374a3eeab79764f9509075b0d8", 1},
{"b26f85ffaf3595ecd9a8b1e0c894f1b9e6e3ed0e8c3f28bcde3d66e63bfedd4d", 2},
{"a49e68b3e2230855586e9ffd1b2962a2282411a488b80e3bd65851f068394c0a", 3},
{"a49e68b3e2230855586e9ffd1b2962a2282411a488b80e3bd65851f068394c0a", 3},
{"a49e68b3e2230855586e9ffd1b2962a2282411a488b80e3bd65851f068394c0a", 3},
{"864f78102661c05b61cafcb59785349fd2fb7a956ec00a77198fe5bc2432de76", 4},
{"9bcc1718757db298fb656ae6e2ee143dde746f49fbf6805db7683cb574c36728", 1},
{"33ccead640727d66c62be03e089a3ca3f4ef7c374a3eeab79764f9509075b0d8", 2},
{"33ccead640727d66c62be03e089a3ca3f4ef7c374a3eeab79764f9509075b0d8", 2},
{"b26f85ffaf3595ecd9a8b1e0c894f1b9e6e3ed0e8c3f28bcde3d66e63bfedd4d", 3},
{"a49e68b3e2230855586e9ffd1b2962a2282411a488b80e3bd65851f068394c0a", 4},
{"a49e68b3e2230855586e9ffd1b2962a2282411a488b80e3bd65851f068394c0a", 4},
{"a49e68b3e2230855586e9ffd1b2962a2282411a488b80e3bd65851f068394c0a", 4},
{"0000000000000000000000000000000000000000000000000000000000000000", 0},
{"864f78102661c05b61cafcb59785349fd2fb7a956ec00a77198fe5bc2432de76", 5},
}
for _, tc := range testCases {
+43 -17
View File
@@ -9,8 +9,7 @@ import (
"fmt"
"io/ioutil"
"os"
"golang.org/x/sys/unix"
"syscall"
)
func main() {
@@ -29,18 +28,30 @@ func main() {
}
func Hashcalc(filename string) string {
key, err := hmac_compute_key(filename)
key, ftype, err := hmac_compute_key(filename)
var result, data []byte
if err != nil {
fmt.Fprintf(os.Stderr, "Error stating file '%s' %v\n", filename, err)
return ""
}
// Only handle files for now..
data, err := ioutil.ReadFile(filename)
if err != nil {
fmt.Fprintf(os.Stderr, "Read error for '%s' %v\n", filename, err)
return ""
switch ftype {
case '-':
data, err = ioutil.ReadFile(filename)
if err != nil {
fmt.Fprintf(os.Stderr, "Read error for '%s' %v\n", filename, err)
return ""
}
case 'd':
data = []byte("DIRECTORY") // fixed magic string
case 'l':
if target, err := os.Readlink(filename); err != nil {
fmt.Fprintf(os.Stderr, "Error readlink file '%s' %v\n", filename, err)
return ""
} else {
data = []byte(target)
}
}
result := hmac_sha256_for_data(key, data)
result = hmac_sha256_for_data(key, data)
return string(result[:])
}
@@ -71,21 +82,36 @@ func set(out []byte, in int64) {
}
}
// return what should be an ascii string as an array of byte
func hmac_compute_key(filename string) ([]byte, error) {
// hmac_compute_key returns what should be an ascii string as an array of byte
// it is really ugly to be compatible with the C implementation. It is not portable
// as the C version isn't portable.
func hmac_compute_key(filename string) ([]byte, rune, error) {
// Create the key
updatestat := [40]byte{}
var info unix.Stat_t
if err := unix.Stat(filename, &info); err != nil {
return nil, err
var info syscall.Stat_t
if err := syscall.Lstat(filename, &info); err != nil {
return nil, 'x', err
}
ftype := '-'
// Get magic constants out of /usr/include/bits/stat.h
switch info.Mode & 0170000 {
case 0100000: // Regular file
case 0040000: // Directory
ftype = 'd'
info.Size = 0
case 0120000:
ftype = 'l'
info.Mode = 0
default:
return nil, 'x', fmt.Errorf("%s is not a file, directory or symlink %o", filename, info.Mode&0170000)
}
set(updatestat[24:32], 0)
set(updatestat[0:8], int64(info.Mode))
set(updatestat[8:16], int64(info.Uid))
set(updatestat[16:24], int64(info.Gid))
// 24:32 is rdev, but this is always zero
set(updatestat[32:40], int64(info.Size))
set(updatestat[24:32], 0)
set(updatestat[32:40], info.Size)
// fmt.Printf("key is %v\n", updatestat)
key := hmac_sha256_for_data(updatestat[:], nil)
return key, nil
return key, ftype, nil
}