/
aes.go
129 lines (108 loc) Β· 2.93 KB
/
aes.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package caes
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"errors"
"fmt"
"io"
"golang.org/x/crypto/scrypt"
)
type AEScrypter struct{}
func New() *AEScrypter {
return new(AEScrypter)
}
// using scrypt for brute force resistance
func (a *AEScrypter) Encrypt(data []byte, password string) ([]byte, error) {
// transform text password into appropriate 32 byte key for AES
salt, err := aesSaltGen()
if err != nil {
return nil, err
}
key, err := aesDeriveKey([]byte(password), salt)
if err != nil {
return nil, err
}
// generate a new aes cipher using our 32 byte long key
c, err := aes.NewCipher([]byte(key))
if err != nil {
return nil, err
}
// gcm or Galois/Counter Mode, is a mode of operation
// for symmetric key cryptographic block ciphers
// - https://en.wikipedia.org/wiki/Galois/Counter_Mode
gcm, err := cipher.NewGCM(c)
// if any error generating new GCM
// handle them
if err != nil {
return nil, err
}
// creates a new byte array the size of the nonce
// which must be passed to Seal
nonce := make([]byte, gcm.NonceSize())
// populates our nonce with a cryptographically secure
// random sequence
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}
// encrypt our data using the gsm Seal
cipher := gcm.Seal(nonce, nonce, data, nil)
// add salt at the end to get in back on decoding
cipher = append(cipher, salt...)
return cipher, nil
}
func (a *AEScrypter) Decrypt(data []byte, password string) ([]byte, error) {
// data has salt already
// check input text length
if len(data) < 32 {
return nil, errors.New("invalid data len")
}
// fmt.Printf("data len: %d\n", len(data))
// get salt from the end
salt, ciphertext := data[len(data)-32:], data[:len(data)-32]
key, err := aesDeriveKey([]byte(password), salt)
if err != nil {
return nil, err
}
// generate a new aes cipher using our 32 byte long key
c, err := aes.NewCipher([]byte(key))
if err != nil {
return nil, err
}
// gcm or Galois/Counter Mode, is a mode of operation
// for symmetric key cryptographic block ciphers
// - https://en.wikipedia.org/wiki/Galois/Counter_Mode
gcm, err := cipher.NewGCM(c)
if err != nil {
return nil, err
}
// get the nonce size
nonceSize := gcm.NonceSize()
if len(ciphertext) < nonceSize {
return nil, fmt.Errorf("ciphertext too short, nonceSize: %d", nonceSize)
}
// extract our nonce from our encrypted text
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
plain, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, err
}
return plain, nil
}
func aesSaltGen() ([]byte, error) {
salt := make([]byte, 32)
if _, err := rand.Read(salt); err != nil {
return nil, err
}
return salt, nil
}
func aesDeriveKey(password, salt []byte) ([]byte, error) {
// minimum N is 16384
// x32 will take about 2 sec
n := 16384 * 32
key, err := scrypt.Key(password, salt, n, 8, 1, 32)
if err != nil {
return nil, err
}
return key, nil
}