147 lines
3.1 KiB
Go
147 lines
3.1 KiB
Go
|
package secret
|
|||
|
|
|||
|
import (
|
|||
|
"bytes"
|
|||
|
"crypto/aes"
|
|||
|
"crypto/cipher"
|
|||
|
"crypto/rand"
|
|||
|
"encoding/base64"
|
|||
|
"errors"
|
|||
|
"io"
|
|||
|
"strings"
|
|||
|
|
|||
|
. "gitea.zaclys.com/bvaudour/gob/option"
|
|||
|
"golang.org/x/crypto/blowfish"
|
|||
|
)
|
|||
|
|
|||
|
const (
|
|||
|
AES = "aes"
|
|||
|
BLOWFISH = "blowfish"
|
|||
|
)
|
|||
|
|
|||
|
type CipherFunc func([]byte) (cipher.Block, error)
|
|||
|
|
|||
|
var (
|
|||
|
encoders = map[string]CipherFunc{
|
|||
|
AES: aes.NewCipher,
|
|||
|
BLOWFISH: func(key []byte) (c cipher.Block, err error) {
|
|||
|
c, err = blowfish.NewCipher(key)
|
|||
|
return
|
|||
|
},
|
|||
|
}
|
|||
|
blockSize = map[string]int{
|
|||
|
AES: aes.BlockSize,
|
|||
|
BLOWFISH: blowfish.BlockSize,
|
|||
|
}
|
|||
|
)
|
|||
|
|
|||
|
func addBase64Padding(value string) string {
|
|||
|
m := len(value) % 4
|
|||
|
if m != 0 {
|
|||
|
value += strings.Repeat("=", 4-m)
|
|||
|
}
|
|||
|
|
|||
|
return value
|
|||
|
}
|
|||
|
|
|||
|
func encodeToBase64[T ~[]byte | ~string](value []byte) T {
|
|||
|
encoded := strings.Replace(base64.URLEncoding.EncodeToString(value), "=", "", -1)
|
|||
|
return T(encoded)
|
|||
|
}
|
|||
|
|
|||
|
func decodeFromBase64[T ~[]byte | ~string](value T) Result[[]byte] {
|
|||
|
decoded, err := base64.URLEncoding.DecodeString(addBase64Padding(string(value)))
|
|||
|
if err != nil {
|
|||
|
return Err[[]byte](err)
|
|||
|
}
|
|||
|
return Ok([]byte(decoded))
|
|||
|
}
|
|||
|
|
|||
|
func pad(src []byte, bs int) []byte {
|
|||
|
padding := bs - len(src)%bs
|
|||
|
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
|||
|
return append(src, padtext...)
|
|||
|
}
|
|||
|
|
|||
|
func unpad(src []byte) Result[[]byte] {
|
|||
|
length := len(src)
|
|||
|
unpadding := int(src[length-1])
|
|||
|
|
|||
|
if unpadding > length {
|
|||
|
return Err[[]byte](errors.New("unpad error. This could happen when incorrect encryption key is used"))
|
|||
|
}
|
|||
|
|
|||
|
return Ok(src[:(length - unpadding)])
|
|||
|
}
|
|||
|
|
|||
|
// Encrypt chiffre une donnée d’entrée (avec une clé aléatoire) selon
|
|||
|
// l’algorithme demandé. Les algorithmes supportés sont :
|
|||
|
// - blowfish
|
|||
|
// - aes
|
|||
|
func Encrypt[T ~[]byte | ~string](data, key T, algorithm string) Result[T] {
|
|||
|
c, ok := encoders[algorithm]
|
|||
|
if !ok {
|
|||
|
return Err[T](errAlgorithm(algorithm))
|
|||
|
}
|
|||
|
|
|||
|
block, err := c([]byte(key))
|
|||
|
if err != nil {
|
|||
|
return Err[T](err)
|
|||
|
}
|
|||
|
|
|||
|
bs := blockSize[algorithm]
|
|||
|
msg := pad([]byte(data), bs)
|
|||
|
cipherText := make([]byte, bs+len(msg))
|
|||
|
iv := cipherText[:bs]
|
|||
|
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
|||
|
return Err[T](err)
|
|||
|
}
|
|||
|
|
|||
|
cfb := cipher.NewCFBEncrypter(block, iv)
|
|||
|
cfb.XORKeyStream(cipherText[bs:], msg)
|
|||
|
|
|||
|
return Ok(encodeToBase64[T](cipherText))
|
|||
|
}
|
|||
|
|
|||
|
// Decrypte déchiffre une donnée d’entrée (avec une clé aléatoire) selon
|
|||
|
// l’algorithme demandé. Les algorithmes supportés sont :
|
|||
|
// - blowfish
|
|||
|
// - aes
|
|||
|
func Decrypt[T ~[]byte | ~string](encrypted, key T, algorithm string) Result[T] {
|
|||
|
c, ok := encoders[algorithm]
|
|||
|
if !ok {
|
|||
|
return Err[T](errAlgorithm(algorithm))
|
|||
|
}
|
|||
|
|
|||
|
block, err := c([]byte(key))
|
|||
|
if err != nil {
|
|||
|
return Err[T](err)
|
|||
|
}
|
|||
|
|
|||
|
decoded := decodeFromBase64(encrypted)
|
|||
|
if err, ok := decoded.Err(); ok {
|
|||
|
return Err[T](err)
|
|||
|
}
|
|||
|
|
|||
|
dec, _ := decoded.Ok()
|
|||
|
bs := blockSize[algorithm]
|
|||
|
|
|||
|
if (len(dec) % bs) != 0 {
|
|||
|
return Err[T](errors.New("blocksize must be multiple of data length"))
|
|||
|
}
|
|||
|
|
|||
|
iv := dec[:bs]
|
|||
|
msg := dec[bs:]
|
|||
|
|
|||
|
cfb := cipher.NewCFBDecrypter(block, iv)
|
|||
|
cfb.XORKeyStream(msg, msg)
|
|||
|
|
|||
|
unpadMsg := unpad(msg)
|
|||
|
if err, ok := unpadMsg.Err(); ok {
|
|||
|
return Err[T](err)
|
|||
|
}
|
|||
|
|
|||
|
result, _ := unpadMsg.Ok()
|
|||
|
return Ok(T(result))
|
|||
|
}
|