mirror of
https://github.com/rwinkhart/rcw.git
synced 2026-09-05 08:37:27 -04:00
Significant hardening (always work on bytes; zeroize everything); address JetBrains warnings
This commit is contained in:
@@ -4,6 +4,8 @@ import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"errors"
|
||||
|
||||
"github.com/rwinkhart/go-boilerplate/security"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -22,6 +24,7 @@ func encryptAES(decBytes, key2, salt2 []byte) []byte {
|
||||
|
||||
// generate a random nonce
|
||||
nonce := getRandomBytes(nonceSizeAES)
|
||||
defer security.ZeroizeBytes(nonce)
|
||||
|
||||
// encrypt the data
|
||||
ciphertext := aesGCM.Seal(nil, nonce, decBytes, nil)
|
||||
@@ -46,6 +49,7 @@ func decryptAES(encBytes, key1 []byte) ([]byte, error) {
|
||||
|
||||
// create AES-256 cipher
|
||||
block, _ := aes.NewCipher(key2)
|
||||
security.ZeroizeBytes(key2)
|
||||
|
||||
// create GCM mode
|
||||
aesGCM, _ := cipher.NewGCM(block)
|
||||
|
||||
@@ -3,6 +3,7 @@ package wrappers
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/rwinkhart/go-boilerplate/security"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
)
|
||||
|
||||
@@ -17,6 +18,7 @@ func encryptCha(decBytes, key2, salt2 []byte) []byte {
|
||||
|
||||
// generate a random nonce
|
||||
nonce := getRandomBytes(nonceSizeCha)
|
||||
defer security.ZeroizeBytes(nonce)
|
||||
|
||||
// encrypt the data
|
||||
ciphertext := stream.Seal(nil, nonce, decBytes, nil)
|
||||
@@ -41,6 +43,7 @@ func decryptCha(encBytes, key1 []byte) ([]byte, error) {
|
||||
|
||||
// create ChaCha20-Poly1305 cipher
|
||||
stream, _ := chacha20poly1305.NewX(key2)
|
||||
security.ZeroizeBytes(key2)
|
||||
|
||||
// decrypt the data
|
||||
plaintext, err := stream.Open(nil, nonce, ciphertext, nil)
|
||||
|
||||
+21
-6
@@ -1,15 +1,21 @@
|
||||
package wrappers
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
|
||||
// Decrypt decrypts the provided byte slice using the provided passphrase.
|
||||
func Decrypt(encBytes, passphrase []byte) ([]byte, error) {
|
||||
"github.com/rwinkhart/go-boilerplate/security"
|
||||
)
|
||||
|
||||
// DecryptAndZeroizePassphrase decrypts the provided byte slice using the provided passphrase.
|
||||
func DecryptAndZeroizePassphrase(encBytes, passphrase []byte) ([]byte, error) {
|
||||
if len(encBytes) < saltSize1 {
|
||||
return nil, errors.New("High-level decrypt: Encrypted data is too short (invalid Argon2 salt)")
|
||||
return nil, errors.New("high-level decrypt: encrypted data is too short (invalid Argon2 salt)")
|
||||
}
|
||||
salt1 := encBytes[:saltSize1]
|
||||
encBytes = encBytes[saltSize1:]
|
||||
key1 := derivePrimaryKey(passphrase, salt1)
|
||||
security.ZeroizeBytes(passphrase)
|
||||
security.ZeroizeBytes(salt1)
|
||||
var err error
|
||||
encBytes, err = decryptCha(encBytes, key1)
|
||||
if err != nil {
|
||||
@@ -19,19 +25,28 @@ func Decrypt(encBytes, passphrase []byte) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
security.ZeroizeBytes(key1)
|
||||
return encBytes, err
|
||||
}
|
||||
|
||||
// Encrypt encrypts the provided byte slice using the provided passphrase.
|
||||
func Encrypt(decBytes, passphrase []byte) []byte {
|
||||
// EncryptAndZeroizeDecBytesAndPassphrase encrypts the provided byte slice using the provided passphrase.
|
||||
func EncryptAndZeroizeDecBytesAndPassphrase(decBytes, passphrase []byte) []byte {
|
||||
defer security.ZeroizeBytes(decBytes)
|
||||
salt1 := getRandomBytes(saltSize1)
|
||||
defer security.ZeroizeBytes(salt1)
|
||||
salt2AES := getRandomBytes(saltSize2)
|
||||
salt2Cha := getRandomBytes(saltSize2)
|
||||
key1 := derivePrimaryKey(passphrase, salt1)
|
||||
security.ZeroizeBytes(passphrase)
|
||||
key2AES := deriveSecondaryKey(key1, salt2AES, []byte(hkdfInfoAES))
|
||||
key2Cha := deriveSecondaryKey(key1, salt2Cha, []byte(hkdfInfoCha))
|
||||
security.ZeroizeBytes(key1)
|
||||
decBytes = encryptAES(decBytes, key2AES, salt2AES)
|
||||
security.ZeroizeBytes(key2AES)
|
||||
security.ZeroizeBytes(salt2AES)
|
||||
decBytes = encryptCha(decBytes, key2Cha, salt2Cha)
|
||||
security.ZeroizeBytes(key2Cha)
|
||||
security.ZeroizeBytes(salt2Cha)
|
||||
// format: salt1 + decBytes per algorithm (salt2* + nonce + ciphertext)
|
||||
return append(append(make([]byte, 0, saltSize1+len(decBytes)), salt1...), decBytes...)
|
||||
}
|
||||
|
||||
+13
-7
@@ -1,15 +1,19 @@
|
||||
package wrappers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/rwinkhart/go-boilerplate/security"
|
||||
)
|
||||
|
||||
// GenSanityCheck creates an encrypted file containing known plaintext
|
||||
// GenSanityCheckAndZeroizePassphrase creates an encrypted file containing known plaintext
|
||||
// to later be used for ensuring the user does not encrypt data with
|
||||
// an incorrect passphrase.
|
||||
func GenSanityCheck(path string, passphrase []byte) error {
|
||||
err := os.WriteFile(path, Encrypt([]byte("thx4usin'rcw"), passphrase), 0600)
|
||||
func GenSanityCheckAndZeroizePassphrase(path string, passphrase []byte) error {
|
||||
err := os.WriteFile(path, EncryptAndZeroizeDecBytesAndPassphrase([]byte("thx4usin'rcw"), passphrase), 0600)
|
||||
security.ZeroizeBytes(passphrase)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -21,9 +25,11 @@ func RunSanityCheck(path string, passphrase []byte) error {
|
||||
if err != nil {
|
||||
return errors.New("Failed to read sanity check file (" + path + ")")
|
||||
}
|
||||
decBytes, _ := Decrypt(encBytes, passphrase)
|
||||
if string(decBytes) == "thx4usin'rcw" {
|
||||
return nil
|
||||
decBytes, err := DecryptAndZeroizePassphrase(encBytes, passphrase)
|
||||
if err == nil {
|
||||
if bytes.Equal(decBytes, []byte("thx4usin'rcw")) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("Sanity check failed (likely due to inconsistent passphrase)")
|
||||
return errors.New("sanity check failed (likely due to inconsistent passphrase)")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user