Skip to content

Commit

Permalink
Refactor decrypt function (#319)
Browse files Browse the repository at this point in the history
  • Loading branch information
jbygdell authored Jan 26, 2024
2 parents 1cc370f + c34b53d commit c85b3b7
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 157 deletions.
8 changes: 4 additions & 4 deletions .github/integration/tests/tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ else
fi

# Decrypt file
./sda-cli decrypt -key sda_key.sec.pem downloads/data_file.c4gh
C4GH_PASSWORD="" ./sda-cli decrypt -key sda_key.sec.pem downloads/data_file.c4gh

if [ -f downloads/data_file ]; then
echo "Decrypted data file"
Expand Down Expand Up @@ -204,7 +204,7 @@ check_encypted_file "data_file_keys.c4gh"
for key in sda_key sda_key2
do
rm data_file_keys
./sda-cli decrypt -key $key.sec.pem data_file_keys.c4gh
C4GH_PASSWORD="" ./sda-cli decrypt -key $key.sec.pem data_file_keys.c4gh
if [ -f data_file_keys ]; then
echo "Decrypted data file"
else
Expand All @@ -222,7 +222,7 @@ check_encypted_file "data_file_keys.c4gh"
for key in sda_key1 sda_key2
do
rm data_file_keys
./sda-cli decrypt -key $key.sec.pem data_file_keys.c4gh
C4GH_PASSWORD="" ./sda-cli decrypt -key $key.sec.pem data_file_keys.c4gh
if [ -f data_file_keys ]; then
echo "Decrypted data file"
else
Expand All @@ -240,7 +240,7 @@ check_encypted_file "data_file_keys.c4gh"
for key in sda_key sda_key1 sda_key2
do
rm data_file_keys
./sda-cli decrypt -key $key.sec.pem data_file_keys.c4gh
C4GH_PASSWORD="" ./sda-cli decrypt -key $key.sec.pem data_file_keys.c4gh
if [ -f data_file_keys ]; then
echo "Decrypted data file"
else
Expand Down
104 changes: 25 additions & 79 deletions decrypt/decrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@ import (
"github.com/NBISweden/sda-cli/helpers"
"github.com/neicnordic/crypt4gh/keys"
"github.com/neicnordic/crypt4gh/streaming"
log "github.com/sirupsen/logrus"
)

// Help text and command line flags.

// Usage text that will be displayed as command line help text when using the
// `help decrypt` command
var Usage = `
USAGE: %s decrypt -key <private-key-file> [file(s)]
USAGE: %s decrypt -key <private-key-file> (--force-overwrite) [file(s)]
decrypt:
Decrypts files from the Sensitive Data Archive (SDA) with the
Expand All @@ -39,13 +38,12 @@ var ArgHelp = `
// main program help
var Args = flag.NewFlagSet("decrypt", flag.ExitOnError)

var privateKeyFile = Args.String("key", "",
"Private key to use for decrypting files.")
var privateKeyFile = Args.String("key", "", "Private key to use for decrypting files.")
var forceOverwrite = Args.Bool("force-overwrite", false, "Force overwrite existing files.")

// Decrypt takes a set of arguments, parses them, and attempts to decrypt the
// given data files with the given private key file..
func Decrypt(args []string) error {

// Call ParseArgs to take care of all the flag parsing
err := helpers.ParseArgs(args, Args)
if err != nil {
Expand All @@ -57,10 +55,8 @@ func Decrypt(args []string) error {
// All filenames are read into a struct together with their output filenames
files := []helpers.EncryptionFileSet{}
for _, filename := range Args.Args() {

// Set directory for the output file
unencryptedFilename := strings.TrimSuffix(filename, ".c4gh")

files = append(files, helpers.EncryptionFileSet{Encrypted: filename, Unencrypted: unencryptedFilename})
}

Expand All @@ -69,37 +65,38 @@ func Decrypt(args []string) error {
return errors.New("a private key is required to decrypt data")
}

var privateKey *[32]byte

// try reading private key without password
privateKey, err = readPrivateKeyFile(*privateKeyFile, "")
if err != nil {

// if there was an error, try again with the password
password, err := getPassword("C4GH_PASSWORD")
if err != nil {
return err
}

// Loading private key file
privateKey, err = readPrivateKeyFile(*privateKeyFile, password)
password, available := os.LookupEnv("C4GH_PASSWORD")
if !available {
password, err = helpers.PromptPassword("Enter password to unlock private key")
if err != nil {
return err
}
}

// Check that all the encrypted files exist, and all the unencrypted don't
err = checkFiles(files)
// Loading private key file
privateKey, err := readPrivateKeyFile(*privateKeyFile, password)
if err != nil {
return err
}

// Check that all the encrypted files exist, and all the unencrypted don't
for _, file := range files {
// check that the input file exists and is readable
if !helpers.FileIsReadable(file.Encrypted) {
return fmt.Errorf("cannot read input file %s", file.Encrypted)
}

// check that the output file doesn't exist
if helpers.FileExists(file.Unencrypted) && !*forceOverwrite {
return fmt.Errorf("outfile %s already exists", file.Unencrypted)
}
}

// decrypt the input files
numFiles := len(files)
for i, file := range files {
log.Infof("Decrypting file %v/%v: %s", i+1, numFiles, file.Encrypted)

err = decrypt(file.Encrypted, file.Unencrypted, *privateKey)
fmt.Printf("Decrypting file %v/%v: %s\n", i+1, numFiles, file.Encrypted)
err = decryptFile(file.Encrypted, file.Unencrypted, *privateKey)
if err != nil {
return err
}
Expand All @@ -108,31 +105,13 @@ func Decrypt(args []string) error {
return nil
}

// getPassword will check if the `envVar` environment variable is set, and
// return its value if present. Otherwise, the password will be read from a user
// prompt.
func getPassword(envVar string) (string, error) {
// check if there is a password available in the `envVar` env variable
password, available := os.LookupEnv(envVar)
if available {
return password, nil
}

// otherwise, read the password from a user prompt
password, err := helpers.PromptPassword("Enter password to unlock private key")

return password, err
}

// Reads a private key file from a file using the crypt4gh keys module
func readPrivateKeyFile(filename, password string) (key *[32]byte, err error) {

// Check that the file exists
if !helpers.FileExists(filename) {
return nil, fmt.Errorf("private key file %s doesn't exist", filename)
}

log.Info("Reading Private key file")
file, err := os.Open(filepath.Clean(filename))
if err != nil {
return nil, err
Expand All @@ -146,48 +125,15 @@ func readPrivateKeyFile(filename, password string) (key *[32]byte, err error) {
return &privateKey, err
}

// Checks that all the encrypted files exists, and are readable, and that the
// unencrypted files do not exist
func checkFiles(files []helpers.EncryptionFileSet) error {
log.Info("Checking files")
for _, file := range files {
// check that the input file exists and is readable
if !helpers.FileIsReadable(file.Encrypted) {
return fmt.Errorf("cannot read input file %s", file.Encrypted)
}

// check that the output file doesn't exist
if helpers.FileExists(file.Unencrypted) {
return fmt.Errorf("outfile %s already exists", file.Unencrypted)
}
}

return nil
}

// decrypts the data in `filename` with the given `privateKey`, writing the
// resulting data to `outfile`.
func decrypt(filename, outfileName string, privateKey [32]byte) error {

// check that the infile exists, and the the outfile doesn't exist
if !helpers.FileIsReadable(filename) {
return fmt.Errorf("infile %s does not exist or could not be read", filename)
}

if helpers.FileExists(outfileName) {
return fmt.Errorf("outfile %s already exists", outfileName)
}

func decryptFile(filename, outfileName string, privateKey [32]byte) error {
// open input file for reading
inFile, err := os.Open(filepath.Clean(filename))
if err != nil {
return err
}
defer func() {
if err := inFile.Close(); err != nil {
log.Errorf("error closing file: %s\n", err)
}
}()
defer inFile.Close()

// Create crypt4gh reader
crypt4GHReader, err := streaming.NewCrypt4GHReader(inFile, privateKey, nil)
Expand Down
Loading

0 comments on commit c85b3b7

Please sign in to comment.