SEBA-666 Use sha-256 instead of sha-1; verify checksums

Change-Id: I3036b50fa97f898948bb3ec94ff9665bb3fc9752
diff --git a/commands/backup.go b/commands/backup.go
index d5ae100..3ee7abe 100644
--- a/commands/backup.go
+++ b/commands/backup.go
@@ -78,6 +78,7 @@
 	local_name := options.Args.LocalFileName
 
 	// STEP 1: Create backup operation
+
 	backupop := make(map[string]interface{})
 	backupop["operation"] = "create"
 	err = CreateModel(conn, descriptor, "BackupOperation", backupop)
@@ -88,6 +89,7 @@
 	conditional_printf(!options.Quiet, "Waiting for sync ")
 
 	// STEP 2: Wait for the operation to complete
+
 	flags := GM_UNTIL_ENACTED | GM_UNTIL_FOUND | Ternary_uint32(options.Quiet, GM_QUIET, 0)
 	conn, completed_backupop, err := GetModelWithRetry(conn, descriptor, "BackupOperation", backupop["id"].(int32), flags)
 	if err != nil {
@@ -127,7 +129,16 @@
 		return err
 	}
 
-	// STEP 5: Show results
+	// STEP 5: Verify checksum
+
+	if completed_backupfile.GetFieldByName("checksum").(string) != h.GetChecksum() {
+		return fmt.Errorf("Checksum mismatch, received=%s, expected=%s",
+			h.GetChecksum(),
+			completed_backupfile.GetFieldByName("checksum").(string))
+	}
+
+	// STEP 6: Show results
+
 	outputFormat := CharReplacer.Replace(options.Format)
 	if outputFormat == "" {
 		outputFormat = DEFAULT_BACKUP_FORMAT
@@ -140,7 +151,7 @@
 	data[0].Chunks = h.chunks
 	data[0].Bytes = h.bytes
 	data[0].Status = h.status
-	data[0].Checksum = fmt.Sprintf("sha1:%x", h.hash.Sum(nil))
+	data[0].Checksum = h.GetChecksum()
 
 	result := CommandResult{
 		Format:   format.Format(outputFormat),
@@ -166,7 +177,7 @@
 
 	// STEP 1: Upload the file
 
-	upload_result, err := UploadFile(conn, descriptor, local_name, uri, 65536)
+	h, upload_result, err := UploadFile(conn, descriptor, local_name, uri, 65536)
 	if err != nil {
 		return err
 	}
@@ -176,10 +187,20 @@
 		return errors.New("Upload status was " + upload_status)
 	}
 
+	// STEP 2: Verify checksum
+
+	if upload_result.GetFieldByName("checksum").(string) != h.GetChecksum() {
+		return fmt.Errorf("Checksum mismatch, expected=%s, received=%s",
+			h.GetChecksum(),
+			upload_result.GetFieldByName("checksum").(string))
+	}
+
 	// STEP 2: Create a BackupFile object
+
 	backupfile := make(map[string]interface{})
 	backupfile["name"] = remote_name
 	backupfile["uri"] = uri
+	backupfile["checksum"] = h.GetChecksum()
 	err = CreateModel(conn, descriptor, "BackupFile", backupfile)
 	if err != nil {
 		return err
@@ -187,6 +208,7 @@
 	conditional_printf(!options.Quiet, "Created backup file %d\n", backupfile["id"])
 
 	// STEP 3: Create a BackupOperation object
+
 	backupop := make(map[string]interface{})
 	backupop["operation"] = "restore"
 	backupop["file_id"] = backupfile["id"]
@@ -199,6 +221,7 @@
 	conditional_printf(!options.Quiet, "Waiting for completion ")
 
 	// STEP 4: Wait for completion
+
 	flags := GM_UNTIL_ENACTED | GM_UNTIL_FOUND | GM_UNTIL_STATUS | Ternary_uint32(options.Quiet, GM_QUIET, 0)
 	conn, completed_backupop, err := FindModelWithRetry(conn, descriptor, "BackupOperation", "uuid", backupop["uuid"].(string), flags)
 	if err != nil {
@@ -210,6 +233,7 @@
 	conditional_printf(!options.Quiet, "\n")
 
 	// STEP 5: Show results
+
 	outputFormat := CharReplacer.Replace(options.Format)
 	if outputFormat == "" {
 		outputFormat = DEFAULT_BACKUP_FORMAT
diff --git a/commands/transfer.go b/commands/transfer.go
index f41a8b8..819e82f 100644
--- a/commands/transfer.go
+++ b/commands/transfer.go
@@ -18,6 +18,7 @@
 
 import (
 	"errors"
+	"fmt"
 	flags "github.com/jessevdk/go-flags"
 	"github.com/opencord/cordctl/format"
 	"strings"
@@ -83,7 +84,13 @@
 		return errors.New("uri argument should be a file:// uri")
 	}
 
-	d, err := UploadFile(conn, descriptor, local_name, uri, options.ChunkSize)
+	h, upload_result, err := UploadFile(conn, descriptor, local_name, uri, options.ChunkSize)
+
+	if upload_result.GetFieldByName("checksum").(string) != h.GetChecksum() {
+		return fmt.Errorf("Checksum mismatch, expected=%s, received=%s",
+			h.GetChecksum(),
+			upload_result.GetFieldByName("checksum").(string))
+	}
 
 	outputFormat := CharReplacer.Replace(options.Format)
 	if outputFormat == "" {
@@ -94,10 +101,10 @@
 	}
 
 	data := make([]TransferOutput, 1)
-	data[0].Checksum = d.GetFieldByName("checksum").(string)
-	data[0].Chunks = int(d.GetFieldByName("chunks_received").(int32))
-	data[0].Bytes = int(d.GetFieldByName("bytes_received").(int32))
-	data[0].Status = GetEnumValue(d, "status")
+	data[0].Checksum = upload_result.GetFieldByName("checksum").(string)
+	data[0].Chunks = int(upload_result.GetFieldByName("chunks_received").(int32))
+	data[0].Bytes = int(upload_result.GetFieldByName("bytes_received").(int32))
+	data[0].Status = GetEnumValue(upload_result, "status")
 
 	result := CommandResult{
 		Format:   format.Format(outputFormat),
diff --git a/commands/transfer_handler.go b/commands/transfer_handler.go
index 0fce0f6..91e46d3 100644
--- a/commands/transfer_handler.go
+++ b/commands/transfer_handler.go
@@ -18,7 +18,8 @@
 
 import (
 	"context"
-	"crypto/sha1"
+	"crypto/sha256"
+	"fmt"
 	"github.com/fullstorydev/grpcurl"
 	"github.com/golang/protobuf/proto"
 	"github.com/jhump/protoreflect/dynamic"
@@ -44,6 +45,7 @@
 	chunksize int
 	f         *os.File
 	uri       string
+	hash      hash.Hash
 }
 
 func (h *DownloadHandler) OnReceiveResponse(m proto.Message) {
@@ -60,6 +62,10 @@
 	h.bytes += len(chunk)
 }
 
+func (h *DownloadHandler) GetChecksum() string {
+	return fmt.Sprintf("sha256:%x", h.hash.Sum(nil))
+}
+
 func (h *UploadHandler) GetParams(msg proto.Message) error {
 	dmsg, err := dynamic.AsDynamicMessage(msg)
 	if err != nil {
@@ -82,13 +88,20 @@
 		return err
 	}
 
+	chunk := string(block[:bytes_read])
+	io.WriteString(h.hash, chunk)
+
 	dmsg.TrySetFieldByName("uri", h.uri)
-	dmsg.TrySetFieldByName("chunk", string(block[:bytes_read]))
+	dmsg.TrySetFieldByName("chunk", chunk)
 
 	return nil
 }
 
-func UploadFile(conn *grpc.ClientConn, descriptor grpcurl.DescriptorSource, local_name string, uri string, chunkSize int) (*dynamic.Message, error) {
+func (h *UploadHandler) GetChecksum() string {
+	return fmt.Sprintf("sha256:%x", h.hash.Sum(nil))
+}
+
+func UploadFile(conn *grpc.ClientConn, descriptor grpcurl.DescriptorSource, local_name string, uri string, chunkSize int) (*UploadHandler, *dynamic.Message, error) {
 	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
 	defer cancel()
 
@@ -96,21 +109,24 @@
 
 	f, err := os.Open(local_name)
 	if err != nil {
-		return nil, err
+		return nil, nil, err
 	}
 
-	h := &UploadHandler{uri: uri, f: f, chunksize: chunkSize}
+	h := &UploadHandler{uri: uri,
+		f:         f,
+		chunksize: chunkSize,
+		hash:      sha256.New()}
 
 	err = grpcurl.InvokeRPC(ctx, descriptor, conn, "xos.filetransfer/Upload", headers, h, h.GetParams)
 	if err != nil {
-		return nil, err
+		return nil, nil, err
 	}
 	d, err := dynamic.AsDynamicMessage(h.Response)
 	if err != nil {
-		return nil, err
+		return nil, nil, err
 	}
 
-	return d, err
+	return h, d, err
 }
 
 func DownloadFile(conn *grpc.ClientConn, descriptor grpcurl.DescriptorSource, uri string, local_name string) (*DownloadHandler, error) {
@@ -132,7 +148,7 @@
 			Fields: map[string]map[string]interface{}{"xos.FileRequest": dm},
 		},
 		f:      f,
-		hash:   sha1.New(),
+		hash:   sha256.New(),
 		status: "SUCCESS"}
 
 	err = grpcurl.InvokeRPC(ctx, descriptor, conn, "xos.filetransfer/Download", headers, h, h.GetParams)