go_encryption/implementation.go
2024-05-10 21:23:51 +02:00

152 lines
4.2 KiB
Go

package go_encryption
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"encoding/json"
"fmt"
"os"
)
func NewAES128EncryptionProvider() IEncryptionProvider {
key, _ := hex.DecodeString(defaultEncryptionKey)
return &aes128encryption{
encryptionKey: key,
_context: defaultContext,
}
}
type aes128encryption struct {
encryptionKey []byte
_context []byte
}
// SetEncryptionKey allows to modify the encryption key.
func (e *aes128encryption) SetEncryptionKey(key []byte) {
// Make sure we have a valid key length
if keyLen := len(key); keyLen != 16 &&
keyLen != 24 &&
keyLen != 32 {
if keyLen == 0 {
key, _ = hex.DecodeString(defaultEncryptionKey)
} else if keyLen < 32 {
keyHash := sha256.Sum256(key)
key = keyHash[:]
} else {
keyHash := sha512.Sum512_256(key)
key = keyHash[:]
}
}
e.encryptionKey = key
}
// SetContext allows to modify the encryption context.
// N.B.: The context must be between 12 and 16 bytes long.
func (e *aes128encryption) SetContext(context []byte) (err error) {
length := len(context)
if 12 > length || length > 16 {
err = fmt.Errorf("encryption: unsupported context length")
} else {
e._context = e._context[:0]
e._context = append(e._context, context...)
}
return
}
// EncryptData returns the given data as JSON in encrypted form.
func (e *aes128encryption) EncryptData(data interface{}) (encrypted []byte, err error) {
var binary []byte
if binary, err = json.Marshal(data); err == nil {
encrypted, err = e.Encrypt(binary)
}
return
}
// EncryptToFile saves the given data to the given filepath as JSON in encrypted form.
func (e *aes128encryption) EncryptToFile(data interface{}, filepath string) (err error) {
var encrypted []byte
if encrypted, err = e.EncryptData(data); err == nil {
err = os.WriteFile(filepath, encrypted, os.ModePerm)
}
return
}
// DecryptData tries to decrypt the given binary and to unmarshal the resulting JSON to the target interface.
func (e *aes128encryption) DecryptData(binary []byte, target interface{}) (err error) {
var decrypted []byte
if decrypted, err = e.Decrypt(binary); err == nil {
err = json.Unmarshal(decrypted, target)
for i := 0; i < len(decrypted); i++ {
decrypted[i] = 0
}
}
return
}
// DecryptData tries to decrypt the content of the file specified and to unmarshal the resulting JSON to the target interface.
func (e *aes128encryption) DecryptFile(path string, target interface{}) (err error) {
var binary []byte
if binary, err = os.ReadFile(path); err == nil {
err = e.DecryptData(binary, target)
}
return
}
// getCrypter returns an AES cipher interface based on the current 16-, 24- or 32-byte key.
func (e *aes128encryption) getCrypter() (crypter cipher.AEAD, err error) {
var key []byte
key = append(key, e.encryptionKey...)
var block cipher.Block
if block, err = aes.NewCipher(key); err == nil {
crypter, err = cipher.NewGCMWithNonceSize(block, len(e._context))
}
for i := 0; i < len(key); i++ {
key[i] = 0
}
return
}
// getNonce returns a copy of the conext data
func (e *aes128encryption) getNonce() (nonce []byte) {
nonce = append(nonce, e._context...)
return
}
// Encrypt tries to encrypt the given binary with the appropriate AES cipher.
// N.B.: The data in the given binary will be nulled upon successful encryption.
func (e *aes128encryption) Encrypt(binary []byte) (encrypted []byte, err error) {
var crypter cipher.AEAD
if crypter, err = e.getCrypter(); err == nil {
nonce := e.getNonce()
encrypted = crypter.Seal(nonce, nonce, binary, nil)
for i := 0; i < len(binary); i++ {
binary[i] = 0
}
}
return
}
// Decrypt tries to decrypt the given binary with the appropriate AES cipher.
func (e *aes128encryption) Decrypt(binary []byte) (decrypted []byte, err error) {
var crypter cipher.AEAD
if crypter, err = e.getCrypter(); err == nil {
nonceSize := crypter.NonceSize()
if len(binary) > nonceSize {
context, encrypted := binary[:nonceSize], binary[nonceSize:]
nonce := e.getNonce()
if bytes.Equal(nonce, context) {
decrypted, err = crypter.Open(nil, nonce, encrypted, nil)
} else {
err = fmt.Errorf("encryption: bad context")
}
} else {
err = fmt.Errorf("encryption: not enough data")
}
}
return
}