goext/cryptext/aes.go

133 lines
2.5 KiB
Go
Raw Permalink Normal View History

2023-01-29 05:45:29 +01:00
package cryptext
import (
2023-02-03 00:59:54 +01:00
"bytes"
2023-01-29 05:45:29 +01:00
"crypto/aes"
"crypto/cipher"
"crypto/rand"
2023-02-03 00:59:54 +01:00
"crypto/sha256"
"encoding/base32"
"encoding/json"
2023-01-29 05:45:29 +01:00
"errors"
2023-01-29 06:02:58 +01:00
"golang.org/x/crypto/scrypt"
2023-01-29 05:45:29 +01:00
"io"
)
// https://stackoverflow.com/a/18819040/1761622
2023-02-03 00:59:54 +01:00
type aesPayload struct {
2023-02-03 01:05:36 +01:00
Salt []byte `json:"s"`
IV []byte `json:"i"`
Data []byte `json:"d"`
Rounds int `json:"r"`
Version uint `json:"v"`
2023-02-03 00:59:54 +01:00
}
func EncryptAESSimple(password []byte, data []byte, rounds int) (string, error) {
2023-01-29 06:02:58 +01:00
2023-02-03 00:59:54 +01:00
salt := make([]byte, 8)
_, err := io.ReadFull(rand.Reader, salt)
2023-01-29 06:02:58 +01:00
if err != nil {
2023-02-03 00:59:54 +01:00
return "", err
}
key, err := scrypt.Key(password, salt, rounds, 8, 1, 32)
if err != nil {
return "", err
2023-01-29 06:02:58 +01:00
}
2023-01-29 05:45:29 +01:00
block, err := aes.NewCipher(key)
if err != nil {
2023-02-03 00:59:54 +01:00
return "", err
2023-01-29 05:45:29 +01:00
}
2023-01-29 06:02:58 +01:00
2023-02-03 00:59:54 +01:00
h := sha256.New()
h.Write(data)
checksum := h.Sum(nil)
if len(checksum) != 32 {
return "", errors.New("wrong cs size")
}
2023-01-29 06:02:58 +01:00
2023-02-03 00:59:54 +01:00
ciphertext := make([]byte, 32+len(data))
iv := make([]byte, aes.BlockSize)
_, err = io.ReadFull(rand.Reader, iv)
if err != nil {
return "", err
2023-01-29 05:45:29 +01:00
}
2023-01-29 06:02:58 +01:00
2023-02-03 00:59:54 +01:00
combinedData := make([]byte, 0, 32+len(data))
combinedData = append(combinedData, checksum...)
combinedData = append(combinedData, data...)
2023-01-29 05:45:29 +01:00
cfb := cipher.NewCFBEncrypter(block, iv)
2023-02-03 00:59:54 +01:00
cfb.XORKeyStream(ciphertext, combinedData)
pl := aesPayload{
2023-02-03 01:05:36 +01:00
Salt: salt,
IV: iv,
Data: ciphertext,
Version: 1,
Rounds: rounds,
2023-02-03 00:59:54 +01:00
}
jbin, err := json.Marshal(pl)
if err != nil {
return "", err
}
res := base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(jbin)
2023-01-29 06:02:58 +01:00
2023-02-03 00:59:54 +01:00
return res, nil
2023-01-29 05:45:29 +01:00
}
2023-02-03 00:59:54 +01:00
func DecryptAESSimple(password []byte, encText string) ([]byte, error) {
2023-01-29 06:02:58 +01:00
2023-02-03 00:59:54 +01:00
jbin, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(encText)
2023-01-29 06:02:58 +01:00
if err != nil {
return nil, err
}
2023-02-03 00:59:54 +01:00
var pl aesPayload
err = json.Unmarshal(jbin, &pl)
2023-01-29 05:45:29 +01:00
if err != nil {
return nil, err
}
2023-01-29 06:02:58 +01:00
2023-02-03 01:05:36 +01:00
if pl.Version != 1 {
return nil, errors.New("unsupported version")
}
2023-02-03 00:59:54 +01:00
key, err := scrypt.Key(password, pl.Salt, pl.Rounds, 8, 1, 32) // this is not 100% correct, rounds too low and salt is missing
if err != nil {
return nil, err
2023-01-29 05:45:29 +01:00
}
2023-01-29 06:02:58 +01:00
2023-02-03 00:59:54 +01:00
block, err := aes.NewCipher(key)
2023-01-29 05:45:29 +01:00
if err != nil {
return nil, err
}
2023-01-29 06:02:58 +01:00
2023-02-03 00:59:54 +01:00
dest := make([]byte, len(pl.Data))
cfb := cipher.NewCFBDecrypter(block, pl.IV)
cfb.XORKeyStream(dest, pl.Data)
if len(dest) < 32 {
return nil, errors.New("payload too small")
}
chck := dest[:32]
data := dest[32:]
h := sha256.New()
h.Write(data)
chck2 := h.Sum(nil)
if !bytes.Equal(chck, chck2) {
return nil, errors.New("checksum mismatch")
}
2023-01-29 05:45:29 +01:00
return data, nil
}