From 3c9928d4431e57e24f95700bc0c56afd28a6f808 Mon Sep 17 00:00:00 2001 From: Baptiste Ducatel Date: Wed, 8 Jun 2022 12:36:05 +0200 Subject: [PATCH] add key derivation --- actions/backup.go | 4 +-- actions/restore.go | 6 ++-- main.go | 20 ++----------- utils/crypt.go | 75 +++++++++++++++++++++++++++++++++++++++------- 4 files changed, 70 insertions(+), 35 deletions(-) diff --git a/actions/backup.go b/actions/backup.go index 03ba9ae..40e6b42 100644 --- a/actions/backup.go +++ b/actions/backup.go @@ -129,9 +129,7 @@ func BackupActionHandler(ctx domain.ExecutionContext, backupFilesOpt *bool, back return fmt.Errorf("Unable to create the encrypted file: %s\n", err) } - aesKey := []byte(*key) - hmacKey := aesKey - err = utils.Encrypt(infile, outfile, aesKey, hmacKey) + err = utils.Encrypt(infile, outfile, []byte(*key)) if err != nil { return fmt.Errorf("Unable to encrypt file: %s\n", err) } diff --git a/actions/restore.go b/actions/restore.go index a8de403..eb600a1 100644 --- a/actions/restore.go +++ b/actions/restore.go @@ -110,15 +110,13 @@ func decrypt(encryptedFile string, decryptedFile string, key *string) error { log.Fatal(err) } - aesKey := []byte(*key) - hmacKey := aesKey - err = utils.Decrypt(infile, outfile, aesKey, hmacKey) + err = utils.Decrypt(infile, outfile, []byte(*key)) infile.Close() outfile.Close() if err != nil { removeDecryptedFile(decryptedFile) - return fmt.Errorf("Unable to decrypt the file %s\n%s\n", decryptedFile, err) + return fmt.Errorf("Unable to decrypt the file %s\n%s\n", encryptedFile, err) } fmt.Printf("\n %s %s decrypted\n", color.GreenString("✓"), encryptedFile) diff --git a/main.go b/main.go index 235e8ea..ba5a0e7 100644 --- a/main.go +++ b/main.go @@ -294,11 +294,9 @@ func main() { backupDB := cmd.BoolOpt("db", false, "Indicates if DB will be backup") outputFilename := cmd.StringOpt("o output", "", "Set the filename of the tar.gz") - key := cmd.StringOpt("k", "", "the encryption password (length must be 16, 24 or 32 characters for a key of 128, 192 or 256 bits)") + key := cmd.StringOpt("k", "", "the encryption password") cmd.Action = func() { - checkKeyLength(key) - if *quiet == false { backupFiles = nil backupDB = nil @@ -320,13 +318,11 @@ func main() { restoreConfigFiles := cmd.BoolOpt("config-files", false, "Indicates if config files will be restored") restoreFiles := cmd.BoolOpt("files", false, "Indicates if files will be restored") restoreDB := cmd.BoolOpt("db", false, "Indicates if DB will be restored") - key := cmd.StringOpt("k", "", "the encryption password (length must be 16, 24 or 32 characters for a key of 128, 192 or 256 bits)") + key := cmd.StringOpt("k", "", "the encryption password") file := cmd.StringArg("FILE", "", "A pliz backup file (tar.gz)") cmd.Action = func() { - checkKeyLength(key) - if *quiet == false { restoreConfigFiles = nil restoreFiles = nil @@ -428,15 +424,3 @@ func parseAndCheckConfig() { return } } - -func checkKeyLength(key *string) { - if key == nil { - return - } - - length := len(*key) - if length != 16 && length != 24 && length != 32 { - fmt.Printf(" %s The key length must be 16, 24 or 32 \n", color.RedString("✗")) - cli.Exit(1) - } -} diff --git a/utils/crypt.go b/utils/crypt.go index d730324..3914219 100644 --- a/utils/crypt.go +++ b/utils/crypt.go @@ -15,11 +15,14 @@ import ( "encoding/binary" "errors" "io" + + "golang.org/x/crypto/scrypt" ) // hmacSize must be less to BUFFER_SIZE const BUFFER_SIZE int = 16 * 1024 const IV_SIZE int = 16 +const SALT_SIZE int = 32 const V1 byte = 0x1 const hmacSize = sha512.Size @@ -27,7 +30,15 @@ const hmacSize = sha512.Size var ErrInvalidHMAC = errors.New("Invalid HMAC") // Encrypt the stream using the given AES-CTR and SHA512-HMAC key -func Encrypt(in io.Reader, out io.Writer, keyAes, keyHmac []byte) (err error) { +func Encrypt(in io.Reader, out io.Writer, key []byte) (err error) { + keyAes, saltAes, err := DeriveKey(key, nil) + if err != nil { + return err + } + keyHmac, saltHmac, err := DeriveKey(key, nil) + if err != nil { + return err + } iv := make([]byte, IV_SIZE) _, err = rand.Read(iv) @@ -46,14 +57,22 @@ func Encrypt(in io.Reader, out io.Writer, keyAes, keyHmac []byte) (err error) { // Version _, err = out.Write([]byte{V1}) if err != nil { - return + return err } w := io.MultiWriter(out, HMAC) _, err = w.Write(iv) if err != nil { - return + return err + } + _, err = w.Write(saltAes) + if err != nil { + return err + } + _, err = w.Write(saltHmac) + if err != nil { + return err } buf := make([]byte, BUFFER_SIZE) @@ -85,29 +104,49 @@ func Encrypt(in io.Reader, out io.Writer, keyAes, keyHmac []byte) (err error) { // Decrypt the stream and verify HMAC using the given AES-CTR and SHA512-HMAC key // Do not trust the out io.Writer contents until the function returns the result // of validating the ending HMAC hash. -func Decrypt(in io.Reader, out io.Writer, keyAes, keyHmac []byte) (err error) { +func Decrypt(in io.Reader, out io.Writer, key []byte) (err error) { // Read version (up to 0-255) var version int8 err = binary.Read(in, binary.LittleEndian, &version) if err != nil { - return + return err } iv := make([]byte, IV_SIZE) _, err = io.ReadFull(in, iv) if err != nil { - return + return err + } + saltAes := make([]byte, SALT_SIZE) + _, err = io.ReadFull(in, saltAes) + if err != nil { + return err + } + saltHmac := make([]byte, SALT_SIZE) + _, err = io.ReadFull(in, saltHmac) + if err != nil { + return err + } + keyAes, _, err := DeriveKey(key, saltAes) + if err != nil { + return err + } + keyHmac, _, err := DeriveKey(key, saltHmac) + if err != nil { + return err } AES, err := aes.NewCipher(keyAes) if err != nil { - return + return err } ctr := cipher.NewCTR(AES, iv) h := hmac.New(sha512.New, keyHmac) h.Write(iv) + h.Write(saltAes) + h.Write(saltHmac) mac := make([]byte, hmacSize) w := out @@ -118,7 +157,7 @@ func Decrypt(in io.Reader, out io.Writer, keyAes, keyHmac []byte) (err error) { for { b, err = buf.Peek(BUFFER_SIZE) if err != nil && err != io.EOF { - return + return err } limit = len(b) - hmacSize @@ -145,12 +184,12 @@ func Decrypt(in io.Reader, out io.Writer, keyAes, keyHmac []byte) (err error) { outBuf := make([]byte, int64(limit)) _, err = buf.Read(b[:limit]) if err != nil { - return + return err } ctr.XORKeyStream(outBuf, b[:limit]) _, err = w.Write(outBuf) if err != nil { - return + return err } if err == io.EOF { @@ -164,3 +203,19 @@ func Decrypt(in io.Reader, out io.Writer, keyAes, keyHmac []byte) (err error) { return nil } + +func DeriveKey(password, salt []byte) ([]byte, []byte, error) { + if salt == nil { + salt = make([]byte, SALT_SIZE) + if _, err := rand.Read(salt); err != nil { + return nil, nil, err + } + } + + key, err := scrypt.Key(password, salt, 32768, 8, 1, 32) + if err != nil { + return nil, nil, err + } + + return key, salt, nil +}