Skip to content

Commit f489a95

Browse files
rnbenrnben
authored andcommitted
Initial commit
0 parents  commit f489a95

File tree

10 files changed

+397
-0
lines changed

10 files changed

+397
-0
lines changed

.github/workflows/go.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
name: go-test
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
pull_request:
8+
branches:
9+
- master
10+
11+
jobs:
12+
build:
13+
name: Test
14+
runs-on: ubuntu-latest
15+
steps:
16+
# Checkout your project with git
17+
- name: Checkout
18+
uses: actions/checkout@v2
19+
20+
# Install Go on the VM running the action.
21+
- name: Set up Go
22+
uses: actions/setup-go@v2
23+
with:
24+
go-version: 1.16
25+
26+
- name: Install gocover-cobertura
27+
run: |
28+
go install github.com/boumenot/gocover-cobertura@latest
29+
30+
# Alternatively, install using go install
31+
- name: Set up gotestfmt
32+
run: |
33+
go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest
34+
35+
# Run tests with nice formatting. Save the original log in /tmp/gotest.log
36+
- name: Run tests
37+
run: |
38+
set -euo pipefail
39+
go test -json -v -coverprofile=coverage.txt -v ./... 2>&1 | tee /tmp/gotest.log | gotestfmt

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# README
2+
3+
Replicating MySQL DES Encryption Methods With Go
4+
5+
[The result from des_encrypt is different from Java DESede Algorithm](https://bugs.mysql.com/bug.php?id=49249)

base64.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package mysqlfuncs
2+
3+
import "encoding/base64"
4+
5+
// ToBase64 MySQL TO_BASE64
6+
func ToBase64(b []byte) string {
7+
return base64.StdEncoding.EncodeToString(b)
8+
}
9+
10+
// FromBase64 MySQL FROM_BASE64
11+
func FromBase64(b string) ([]byte, error) {
12+
if b == "" {
13+
return nil, nil
14+
}
15+
16+
return base64.StdEncoding.DecodeString(b)
17+
}

des_decrypt.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package mysqlfuncs
2+
3+
import (
4+
"crypto/cipher"
5+
"crypto/des"
6+
"crypto/md5"
7+
"errors"
8+
"fmt"
9+
"github.com/rnben/mysql-funcs-go/openssl"
10+
)
11+
12+
// DesDecrypt MySQL DES_DECRYPT
13+
func DesDecrypt(encrypted []byte, plainKey string) (res string, err error) {
14+
defer func() {
15+
if r := recover(); r != nil {
16+
err = fmt.Errorf("err: %v", r)
17+
return
18+
}
19+
}()
20+
21+
length := len(encrypted)
22+
23+
if length == 0 {
24+
return "", nil
25+
}
26+
if encrypted[0] != 0xff {
27+
return "", errors.New("invalid encrypted text")
28+
}
29+
30+
encrypted = encrypted[1:]
31+
if len(encrypted)%8 != 0 {
32+
return "", errors.New("invalid encrypted text")
33+
}
34+
35+
iv := []byte{0, 0, 0, 0, 0, 0, 0, 0}
36+
key, _ := openssl.EVPBytesToKey(24, 8, md5.New(), nil, []byte(plainKey), 1)
37+
38+
block, err := des.NewTripleDESCipher(key)
39+
if err != nil {
40+
return "", err
41+
}
42+
43+
blockMode := cipher.NewCBCDecrypter(block, iv)
44+
decrypted := make([]byte, length-1)
45+
blockMode.CryptBlocks(decrypted, encrypted)
46+
return string(unPadding(decrypted)), nil
47+
}
48+
49+
func unPadding(decrypted []byte) []byte {
50+
length := len(decrypted)
51+
tail := int(decrypted[length-1])
52+
return decrypted[:(length - tail)]
53+
}

des_encrypt.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package mysqlfuncs
2+
3+
import (
4+
"bytes"
5+
"crypto/cipher"
6+
"crypto/des"
7+
"crypto/md5"
8+
"fmt"
9+
"github.com/rnben/mysql-funcs-go/openssl"
10+
)
11+
12+
// DesEncrypt MySQL DES_ENCRYPT
13+
func DesEncrypt(plainText, plainKey string) (res []byte, err error) {
14+
defer func() {
15+
if r := recover(); r != nil {
16+
err = fmt.Errorf("panic: %v", r)
17+
return
18+
}
19+
}()
20+
21+
if plainText == "" {
22+
return nil, nil
23+
}
24+
25+
orgLength := len(plainText)
26+
resLength := orgLength + (8 - (orgLength % 8))
27+
28+
iv := []byte{0, 0, 0, 0, 0, 0, 0, 0}
29+
text := padding([]byte(plainText), 8)
30+
key, _ := openssl.EVPBytesToKey(24, 8, md5.New(), nil, []byte(plainKey), 1)
31+
32+
block, err := des.NewTripleDESCipher(key)
33+
if err != nil {
34+
return nil, err
35+
}
36+
37+
blockMode := cipher.NewCBCEncrypter(block, iv)
38+
cipherText := make([]byte, resLength)
39+
blockMode.CryptBlocks(cipherText, text)
40+
41+
cipherText = append([]byte{255}, cipherText...)
42+
43+
return cipherText, nil
44+
}
45+
46+
func padding(ciphertext []byte, blockSize int) []byte {
47+
tail := blockSize - len(ciphertext)%blockSize
48+
padText := bytes.Repeat([]byte("*"), tail)
49+
padText[len(padText)-1] = byte(tail)
50+
return append(ciphertext, padText...)
51+
}

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/rnben/mysql-funcs-go
2+
3+
go 1.16

go.sum

Whitespace-only changes.

openssl/evp.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package openssl
2+
3+
import "hash"
4+
5+
func EVPBytesToKey(keyLen int, ivLen int, md hash.Hash, salt []byte, data []byte, count int) ([]byte, []byte) {
6+
key := make([]byte, keyLen)
7+
keyIx := 0
8+
iv := make([]byte, ivLen)
9+
ivIx := 0
10+
var mdBuf []byte
11+
nkey := keyLen
12+
niv := ivLen
13+
i := 0
14+
if data == nil {
15+
return key, iv
16+
}
17+
18+
addmd := 0
19+
for {
20+
md.Reset()
21+
if addmd > 0 {
22+
md.Write(mdBuf)
23+
}
24+
addmd++
25+
md.Write(data)
26+
if salt != nil {
27+
md.Write(salt[:8])
28+
}
29+
mdBuf = md.Sum(nil)
30+
for i = 1; i < count; i++ {
31+
md.Reset()
32+
md.Write(mdBuf)
33+
mdBuf = md.Sum(nil)
34+
}
35+
i = 0
36+
if nkey > 0 {
37+
for {
38+
if nkey == 0 {
39+
break
40+
}
41+
if i == len(mdBuf) {
42+
break
43+
}
44+
key[keyIx] = mdBuf[i]
45+
keyIx++
46+
nkey--
47+
i++
48+
}
49+
}
50+
if niv > 0 && i != len(mdBuf) {
51+
for {
52+
if niv == 0 {
53+
break
54+
}
55+
if i == len(mdBuf) {
56+
break
57+
}
58+
iv[ivIx] = mdBuf[i]
59+
ivIx++
60+
niv--
61+
i++
62+
}
63+
}
64+
if nkey == 0 && niv == 0 {
65+
break
66+
}
67+
}
68+
for i = 0; i < len(mdBuf); i++ {
69+
mdBuf[i] = 0
70+
}
71+
72+
return key, iv
73+
}

tests/des_decrypt_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package tests
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
7+
. "github.com/rnben/mysql-funcs-go"
8+
)
9+
10+
func TestDesDecrypt(t *testing.T) {
11+
type args struct {
12+
encrypted string
13+
plainKey string
14+
}
15+
tests := []struct {
16+
name string
17+
args args
18+
want string
19+
wantErr bool
20+
}{
21+
{
22+
name: "test-01",
23+
args: args{
24+
plainKey: "1234554321",
25+
encrypted: "/4XOZReziOTR",
26+
},
27+
want: "d123456",
28+
},
29+
{
30+
name: "test-02",
31+
args: args{
32+
plainKey: "1234554321",
33+
encrypted: "/xz9BQ1Ut+OK30k9ll72Nvs=",
34+
},
35+
want: "system123456",
36+
},
37+
{
38+
name: "test-03",
39+
args: args{
40+
plainKey: "1234554321",
41+
encrypted: "",
42+
},
43+
want: "",
44+
},
45+
{
46+
name: "test-04",
47+
args: args{
48+
plainKey: "",
49+
encrypted: "",
50+
},
51+
want: "",
52+
},
53+
{
54+
name: "test-05",
55+
args: args{
56+
plainKey: "",
57+
encrypted: "/x5b4HgjdjYm",
58+
},
59+
want: "hello",
60+
},
61+
{
62+
name: "test-06",
63+
args: args{
64+
plainKey: "",
65+
encrypted: "aGVsbG9oZWxs",
66+
},
67+
want: "",
68+
wantErr: true, // invalid encrypted text
69+
},
70+
}
71+
for _, tt := range tests {
72+
t.Run(tt.name, func(t *testing.T) {
73+
encryptedBytes, _ := FromBase64(tt.args.encrypted)
74+
got, err := DesDecrypt(encryptedBytes, tt.args.plainKey)
75+
if (err != nil) != tt.wantErr {
76+
t.Errorf("DESDecrypt() error = %v, wantErr %v", err, tt.wantErr)
77+
}
78+
if !reflect.DeepEqual(got, tt.want) {
79+
t.Errorf("DesDecrypt() = %v, want %v", got, tt.want)
80+
}
81+
})
82+
}
83+
}

0 commit comments

Comments
 (0)