rafaeljusto / toglacier

Periodic send data to the cloud

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Encrypt backups

rafaeljusto opened this issue · comments

Encrypt backup when sending to the cloud and decrypt it when retrieving. Do this only if there's a symmetric key defined.

https://golang.org/src/crypto/cipher/example_test.go#L264

We should check the performance when encrypting large backup files. Also, we need to improve the algorithm:

Note that this example is simplistic in that it omits any
authentication of the encrypted data. If you were actually to use
StreamReader in this manner, an attacker could flip arbitrary bits in
the output.

Reminder: We could be trying to decrypt a file that was not encrypted in the first place. So maybe we should ignore the decryption errors and return the file as it is.

Not in time for version v2.0.0

An idea would be encrypting/decryoting in chunks using GCM:

func encrypt(filename string, secret string) (encryptedFilename string, err error) {
	archive, err := os.Open(filename)
	if err != nil {
		return "", err
	}
	defer archive.Close()

	encryptedArchive, err := ioutil.TempFile("", "toglacier-")
	if err != nil {
		return "", err
	}
	defer encryptedArchive.Close()

	block, err := aes.NewCipher([]byte(secret))
	if err != nil {
		return "", err
	}

	aesgcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", err
	}

	for {
		chunk := make([]byte, 1048576) // 1MB
		n, err := archive.Read(chunk)

		if err == io.EOF {
			break
		} else if err != nil {
			return "", err
		}

		chunk = chunk[:n]

		nonce := make([]byte, aesgcm.NonceSize())
		if _, err := io.ReadFull(RandomSource, nonce); err != nil {
			return "", err
		}

		encryptedChunk := aesgcm.Seal(nil, nonce, chunk, nil)

		if _, err := encryptedArchive.Write(nonce); err != nil {
			return "", err
		}
		if _, err := encryptedArchive.Write(encryptedChunk); err != nil {
			return "", err
		}
	}

	return encryptedArchive.Name(), nil
}

func decrypt(encryptedFilename string, secret string) (filename string, err error) {
	encryptedArchive, err := os.Open(encryptedFilename)
	if err != nil {
		return "", err
	}
	defer encryptedArchive.Close()

	archive, err := ioutil.TempFile("", "toglacier-")
	if err != nil {
		return "", err
	}
	defer archive.Close()

	block, err := aes.NewCipher([]byte(secret))
	if err != nil {
		return "", err
	}

	aesgcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", err
	}

	for {
		nonce := make([]byte, aesgcm.NonceSize())
		n, err := archive.Read(nonce)

		if err == io.EOF {
			break
		} else if err != nil {
			return "", err
		}

		nonce = nonce[:n]

		chunk := make([]byte, 1048576) // 1MB
		n, err = archive.Read(chunk)

		if err == io.EOF {
			break
		} else if err != nil {
			return "", err
		}

		chunk = chunk[:n]

		decryptedChunk, err := aesgcm.Open(nil, nonce, chunk, nil)
		if err != nil {
			return "", err
		}

		if _, err := archive.Write(decryptedChunk); err != nil {
			return "", err
		}
	}

	return archive.Name(), nil
}

Reminder: We could be trying to decrypt a file that was not encrypted in the first place. So maybe we should ignore the decryption errors and return the file as it is.

Done. We added an encrypted: label at the beginning of the archive to identify when the content can be decrypted.

We should check the performance when encrypting large backup files. Also, we need to improve the algorithm:

Note that this example is simplistic in that it omits any
authentication of the encrypted data. If you were actually to use
StreamReader in this manner, an attacker could flip arbitrary bits in
the output.

To add authentication HMAC-SHA256 we need to read the file content before encrypting, than we could append the hash result to the encrypted data so it's possible to validate later when we decrypt.