Significant hardening (always work on bytes; zeroize everything); address JetBrains warnings

This commit is contained in:
2026-02-10 00:33:56 -05:00
parent 4fd72b0042
commit c5e259a446
9 changed files with 75 additions and 28 deletions
+4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)")
}