/
helper.go
74 lines (59 loc) · 1.4 KB
/
helper.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
package jsontostruct
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"encoding/binary"
"errors"
"io"
"unicode/utf16"
)
var (
magicNum uint32 = 0xBAADF00D
passphrase string
)
const sql_v2 = 0x02
func SetPasshrase(p string) {
passphrase = p
}
func passphraseToKey() []byte {
var runes []rune
for _, l := range passphrase {
runes = append(runes, l)
}
windowsPhrase := utf16.Encode(runes)
var keybuf bytes.Buffer
binary.Write(&keybuf, binary.LittleEndian, windowsPhrase)
key := sha256.Sum256(keybuf.Bytes())
return key[:]
}
func DecryptByPassphrase(cyphertext []byte) (string, error) {
// Only support V2 (AES)
if len(cyphertext) > 0 {
if cyphertext[0] != sql_v2 {
return "", errors.New("required sql v2")
}
} else {
return "", errors.New("chyphertext is empty")
}
key := passphraseToKey()
block, e := aes.NewCipher(key)
if e != nil {
return "", e
}
iv := make([]byte, aes.BlockSize)
io.ReadFull(rand.Reader, iv)
mode := cipher.NewCBCDecrypter(block, cyphertext[4:20])
dst := make([]byte, len(cyphertext)-20)
mode.CryptBlocks(dst, cyphertext[20:])
if binary.LittleEndian.Uint32(dst[:4]) != magicNum {
return "", errors.New("magic bytes failed")
}
if binary.LittleEndian.Uint16(dst[4:6]) != 0 {
return "", errors.New("authenticator unsupported")
}
ptLen := binary.LittleEndian.Uint16(dst[6:8])
return string(dst[8 : 8+ptLen]), nil
}