152 lines
4.2 KiB
Go
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
|
|
}
|