package cryptext import ( "bytes" "crypto/aes" "crypto/cipher" "crypto/rand" "crypto/sha256" "encoding/base32" "encoding/json" "errors" "golang.org/x/crypto/scrypt" "io" ) // https://stackoverflow.com/a/18819040/1761622 type aesPayload struct { Salt []byte `json:"s"` IV []byte `json:"i"` Data []byte `json:"d"` Rounds int `json:"r"` Version uint `json:"v"` } func EncryptAESSimple(password []byte, data []byte, rounds int) (string, error) { salt := make([]byte, 8) _, err := io.ReadFull(rand.Reader, salt) if err != nil { return "", err } key, err := scrypt.Key(password, salt, rounds, 8, 1, 32) if err != nil { return "", err } block, err := aes.NewCipher(key) if err != nil { return "", err } h := sha256.New() h.Write(data) checksum := h.Sum(nil) if len(checksum) != 32 { return "", errors.New("wrong cs size") } ciphertext := make([]byte, 32+len(data)) iv := make([]byte, aes.BlockSize) _, err = io.ReadFull(rand.Reader, iv) if err != nil { return "", err } combinedData := make([]byte, 0, 32+len(data)) combinedData = append(combinedData, checksum...) combinedData = append(combinedData, data...) cfb := cipher.NewCFBEncrypter(block, iv) cfb.XORKeyStream(ciphertext, combinedData) pl := aesPayload{ Salt: salt, IV: iv, Data: ciphertext, Version: 1, Rounds: rounds, } jbin, err := json.Marshal(pl) if err != nil { return "", err } res := base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(jbin) return res, nil } func DecryptAESSimple(password []byte, encText string) ([]byte, error) { jbin, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(encText) if err != nil { return nil, err } var pl aesPayload err = json.Unmarshal(jbin, &pl) if err != nil { return nil, err } if pl.Version != 1 { return nil, errors.New("unsupported version") } 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 } block, err := aes.NewCipher(key) if err != nil { return nil, err } 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") } return data, nil }