-
Notifications
You must be signed in to change notification settings - Fork 62
/
crypto.go
195 lines (177 loc) · 5.15 KB
/
crypto.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
package utils
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"strconv"
"strings"
)
// 16 byte IV
var valIV = "0123456789012345"
// EncryptString creates the base64 encoded encrypted string using the
// cipherKey.
// It accepts the following parameters:
// cipherKey: cipher key to use to encrypt.
// message: to encrypted.
//
// returns the base64 encoded encrypted string.
func EncryptString(cipherKey string, message string) string {
block, _ := aesCipher(cipherKey)
message = encodeNonASCIIChars(message)
value := []byte(message)
value = padWithPKCS7(value)
blockmode := cipher.NewCBCEncrypter(block, []byte(valIV))
cipherBytes := make([]byte, len(value))
blockmode.CryptBlocks(cipherBytes, value)
return base64.StdEncoding.EncodeToString(cipherBytes)
}
type A struct {
I string
Interface *B
}
type B struct {
Value string
}
// DecryptString decodes encrypted string using the cipherKey
//
// It accepts the following parameters:
// cipherKey: cipher key to use to decrypt.
// message: to encrypted.
//
// returns the unencoded encrypted string,
// error if any.
func DecryptString(cipherKey string, message string) (
retVal interface{}, err error) {
if message == "" {
return "**decrypt error***", errors.New("message is empty")
}
block, aesErr := aesCipher(cipherKey)
if aesErr != nil {
return "***decrypt error***", fmt.Errorf("decrypt error aes cipher: %s", aesErr)
}
value, decodeErr := base64.StdEncoding.DecodeString(message)
if decodeErr != nil {
return "***decrypt error***", fmt.Errorf("decrypt error on decode: %s", decodeErr)
}
decrypter := cipher.NewCBCDecrypter(block, []byte(valIV))
//to handle decryption errors
defer func() {
if r := recover(); r != nil {
retVal, err = "***decrypt error***", fmt.Errorf("decrypt error: %s", r)
}
}()
decrypted := make([]byte, len(value))
decrypter.CryptBlocks(decrypted, value)
val, err := unpadPKCS7(decrypted)
if err != nil {
return "***decrypt error***", fmt.Errorf("decrypt error: %s", err)
}
return fmt.Sprintf("%s", string(val)), nil
}
// aesCipher returns the cipher block
//
// It accepts the following parameters:
// cipherKey: cipher key.
//
// returns the cipher block,
// error if any.
func aesCipher(cipherKey string) (cipher.Block, error) {
key := encryptCipherKey(cipherKey)
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
return block, nil
}
// encryptCipherKey creates the 256 bit hex of the cipher key
//
// It accepts the following parameters:
// cipherKey: cipher key to use to decrypt.
//
// returns the 256 bit hex of the cipher key.
func encryptCipherKey(cipherKey string) []byte {
hash := sha256.New()
hash.Write([]byte(cipherKey))
sha256String := hash.Sum(nil)[:16]
return []byte(hex.EncodeToString(sha256String))
}
// encodeNonAsciiChars creates unicode string of the non-ascii chars.
// It accepts the following parameters:
// message: to parse.
//
// returns the encoded string.
func encodeNonASCIIChars(message string) string {
runeOfMessage := []rune(message)
lenOfRune := len(runeOfMessage)
encodedString := bytes.NewBuffer(make([]byte, 0, lenOfRune))
for i := 0; i < lenOfRune; i++ {
intOfRune := uint16(runeOfMessage[i])
if intOfRune > 127 {
hexOfRune := strconv.FormatUint(uint64(intOfRune), 16)
dataLen := len(hexOfRune)
paddingNum := 4 - dataLen
encodedString.WriteString(`\u`)
for i := 0; i < paddingNum; i++ {
encodedString.WriteString("0")
}
encodedString.WriteString(hexOfRune)
} else {
encodedString.WriteString(string(runeOfMessage[i]))
}
}
return encodedString.String()
}
// getHmacSha256 creates the cipher key hashed against SHA256.
// It accepts the following parameters:
// secretKey: the secret key.
// input: input to hash.
//
// returns the hash.
func GetHmacSha256(secretKey string, input string) string {
hmacSha256 := hmac.New(sha256.New, []byte(secretKey))
hmacSha256.Write([]byte(input))
rawSig := base64.StdEncoding.EncodeToString(hmacSha256.Sum(nil))
signature := strings.Replace(strings.Replace(rawSig, "+", "-", -1), "/", "_", -1)
return signature
}
// padWithPKCS7 pads the data as per the PKCS7 standard
// It accepts the following parameters:
// data: data to pad as byte array.
// returns the padded data as byte array.
func padWithPKCS7(data []byte) []byte {
blocklen := 16
padlen := 1
for ((len(data) + padlen) % blocklen) != 0 {
padlen = padlen + 1
}
pad := bytes.Repeat([]byte{byte(padlen)}, padlen)
return append(data, pad...)
}
// unpadPKCS7 unpads the data as per the PKCS7 standard
// It accepts the following parameters:
// data: data to unpad as byte array.
// returns the unpadded data as byte array.
func unpadPKCS7(data []byte) ([]byte, error) {
blocklen := 16
if len(data)%blocklen != 0 || len(data) == 0 {
return nil, fmt.Errorf("invalid data len %d", len(data))
}
padlen := int(data[len(data)-1])
if padlen > blocklen || padlen == 0 {
return nil, fmt.Errorf("padding is invalid")
}
// check padding
pad := data[len(data)-padlen:]
for i := 0; i < padlen; i++ {
if pad[i] != byte(padlen) {
return nil, fmt.Errorf("padding is invalid")
}
}
return data[:len(data)-padlen], nil
}