Files
tidb/pkg/expression/builtin_encryption_test.go

762 lines
32 KiB
Go

// Copyright 2017 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package expression
import (
"context"
"crypto/md5"
"encoding/hex"
"fmt"
"strings"
"testing"
"github.com/pingcap/tidb/pkg/parser/ast"
"github.com/pingcap/tidb/pkg/parser/auth"
"github.com/pingcap/tidb/pkg/parser/charset"
"github.com/pingcap/tidb/pkg/parser/mysql"
"github.com/pingcap/tidb/pkg/parser/terror"
"github.com/pingcap/tidb/pkg/sessionctx/vardef"
"github.com/pingcap/tidb/pkg/sessionctx/variable"
"github.com/pingcap/tidb/pkg/types"
"github.com/pingcap/tidb/pkg/util/chunk"
"github.com/pingcap/tidb/pkg/util/hack"
"github.com/pingcap/tidb/pkg/util/mock"
"github.com/stretchr/testify/require"
)
var cryptTests = []struct {
chs string
origin any
password any
crypt any
}{
{mysql.DefaultCollationName, "", "", ""},
{mysql.DefaultCollationName, "pingcap", "1234567890123456", "2C35B5A4ADF391"},
{mysql.DefaultCollationName, "pingcap", "asdfjasfwefjfjkj", "351CC412605905"},
{mysql.DefaultCollationName, "pingcap123", "123456789012345678901234", "7698723DC6DFE7724221"},
{mysql.DefaultCollationName, "pingcap#%$%^", "*^%YTu1234567", "8634B9C55FF55E5B6328F449"},
{mysql.DefaultCollationName, "pingcap", "", "4A77B524BD2C5C"},
{mysql.DefaultCollationName, "分布式データベース", "pass1234@#$%%^^&", "80CADC8D328B3026D04FB285F36FED04BBCA0CC685BF78B1E687CE"},
{mysql.DefaultCollationName, "分布式データベース", "分布式7782734adgwy1242", "0E24CFEF272EE32B6E0BFBDB89F29FB43B4B30DAA95C3F914444BC"},
{mysql.DefaultCollationName, "pingcap", "密匙", "CE5C02A5010010"},
{"gbk", "pingcap", "密匙", "E407AC6F691ADE"},
{mysql.DefaultCollationName, "pingcap数据库", "数据库passwd12345667", "36D5F90D3834E30E396BE3226E3B4ED3"},
{"gbk", "pingcap数据库", "数据库passwd12345667", "B4BDBD6EC8346379F42836E2E0"},
{mysql.DefaultCollationName, "数据库5667", 123.435, "B22196D0569386237AE12F8AAB"},
{"gbk", "数据库5667", 123.435, "79E22979BD860EF58229"},
{mysql.DefaultCollationName, nil, "数据库passwd12345667", nil},
}
func TestSQLDecode(t *testing.T) {
ctx := createContext(t)
for _, tt := range cryptTests {
err := ctx.GetSessionVars().SetSystemVarWithoutValidation(vardef.CharacterSetConnection, tt.chs)
require.NoError(t, err)
err = ctx.GetSessionVars().SetSystemVarWithoutValidation(vardef.CollationConnection, tt.chs)
require.NoError(t, err)
f, err := newFunctionForTest(ctx, ast.Decode, primitiveValsToConstants(ctx, []any{tt.origin, tt.password})...)
require.NoError(t, err)
d, err := f.Eval(ctx, chunk.Row{})
require.NoError(t, err)
if !d.IsNull() {
d = toHex(d)
}
require.Equal(t, types.NewDatum(tt.crypt), d)
}
testNullInput(t, ctx, ast.Decode)
}
func TestSQLEncode(t *testing.T) {
ctx := createContext(t)
for _, test := range cryptTests {
err := ctx.GetSessionVars().SetSystemVarWithoutValidation(vardef.CharacterSetConnection, test.chs)
require.NoError(t, err)
err = ctx.GetSessionVars().SetSystemVarWithoutValidation(vardef.CollationConnection, test.chs)
require.NoError(t, err)
var h []byte
if test.crypt != nil {
h, _ = hex.DecodeString(test.crypt.(string))
} else {
h = nil
}
f, err := newFunctionForTest(ctx, ast.Encode, primitiveValsToConstants(ctx, []any{h, test.password})...)
require.NoError(t, err)
d, err := f.Eval(ctx, chunk.Row{})
require.NoError(t, err)
if test.origin != nil {
enc := charset.FindEncoding(test.chs)
result, err := enc.Transform(nil, []byte(test.origin.(string)), charset.OpEncode)
require.NoError(t, err)
require.Equal(t, types.NewCollationStringDatum(string(result), test.chs), d)
} else {
result := types.NewDatum(test.origin)
require.Equal(t, result.GetBytes(), d.GetBytes())
}
}
testNullInput(t, ctx, ast.Encode)
}
var aesTests = []struct {
mode string
origin any
params []any
crypt any
}{
// test for ecb
{"aes-128-ecb", "pingcap", []any{"1234567890123456"}, "697BFE9B3F8C2F289DD82C88C7BC95C4"},
{"aes-128-ecb", "pingcap123", []any{"1234567890123456"}, "CEC348F4EF5F84D3AA6C4FA184C65766"},
{"aes-128-ecb", "pingcap", []any{"123456789012345678901234"}, "6F1589686860C8E8C7A40A78B25FF2C0"},
{"aes-128-ecb", "pingcap", []any{"123"}, "996E0CA8688D7AD20819B90B273E01C6"},
{"aes-128-ecb", "pingcap", []any{123}, "996E0CA8688D7AD20819B90B273E01C6"},
{"aes-128-ecb", nil, []any{123}, nil},
{"aes-192-ecb", "pingcap", []any{"1234567890123456"}, "9B139FD002E6496EA2D5C73A2265E661"},
{"aes-256-ecb", "pingcap", []any{"1234567890123456"}, "F80DCDEDDBE5663BDB68F74AEDDB8EE3"},
// test for cbc
{"aes-128-cbc", "pingcap", []any{"1234567890123456", "1234567890123456"}, "2ECA0077C5EA5768A0485AA522774792"},
{"aes-128-cbc", "pingcap", []any{"123456789012345678901234", "1234567890123456"}, "483788634DA8817423BA0934FD2C096E"},
{"aes-192-cbc", "pingcap", []any{"1234567890123456", "1234567890123456"}, "516391DB38E908ECA93AAB22870EC787"},
{"aes-256-cbc", "pingcap", []any{"1234567890123456", "1234567890123456"}, "5D0E22C1E77523AEF5C3E10B65653C8F"},
{"aes-256-cbc", "pingcap", []any{"12345678901234561234567890123456", "1234567890123456"}, "A26BA27CA4BE9D361D545AA84A17002D"},
{"aes-256-cbc", "pingcap", []any{"1234567890123456", "12345678901234561234567890123456"}, "5D0E22C1E77523AEF5C3E10B65653C8F"},
// test for ofb
{"aes-128-ofb", "pingcap", []any{"1234567890123456", "1234567890123456"}, "0515A36BBF3DE0"},
{"aes-128-ofb", "pingcap", []any{"123456789012345678901234", "1234567890123456"}, "C2A93A93818546"},
{"aes-192-ofb", "pingcap", []any{"1234567890123456", "1234567890123456"}, "FE09DCCF14D458"},
{"aes-256-ofb", "pingcap", []any{"1234567890123456", "1234567890123456"}, "2E70FCAC0C0834"},
{"aes-256-ofb", "pingcap", []any{"12345678901234561234567890123456", "1234567890123456"}, "83E2B30A71F011"},
{"aes-256-ofb", "pingcap", []any{"1234567890123456", "12345678901234561234567890123456"}, "2E70FCAC0C0834"},
// test for cfb
{"aes-128-cfb", "pingcap", []any{"1234567890123456", "1234567890123456"}, "0515A36BBF3DE0"},
{"aes-128-cfb", "pingcap", []any{"123456789012345678901234", "1234567890123456"}, "C2A93A93818546"},
{"aes-192-cfb", "pingcap", []any{"1234567890123456", "1234567890123456"}, "FE09DCCF14D458"},
{"aes-256-cfb", "pingcap", []any{"1234567890123456", "1234567890123456"}, "2E70FCAC0C0834"},
{"aes-256-cfb", "pingcap", []any{"12345678901234561234567890123456", "1234567890123456"}, "83E2B30A71F011"},
{"aes-256-cfb", "pingcap", []any{"1234567890123456", "12345678901234561234567890123456"}, "2E70FCAC0C0834"},
}
func TestAESEncrypt(t *testing.T) {
ctx := createContext(t)
fc := funcs[ast.AesEncrypt]
for _, tt := range aesTests {
err := ctx.GetSessionVars().SetSystemVar(vardef.BlockEncryptionMode, tt.mode)
require.NoError(t, err)
args := []types.Datum{types.NewDatum(tt.origin)}
for _, param := range tt.params {
args = append(args, types.NewDatum(param))
}
f, err := fc.getFunction(ctx, datumsToConstants(args))
require.NoError(t, err)
crypt, err := evalBuiltinFunc(f, ctx, chunk.Row{})
require.NoError(t, err)
require.Equal(t, types.NewDatum(tt.crypt), toHex(crypt))
}
err := ctx.GetSessionVars().SetSystemVar(vardef.BlockEncryptionMode, "aes-128-ecb")
require.NoError(t, err)
testNullInput(t, ctx, ast.AesEncrypt)
testAmbiguousInput(t, ctx, ast.AesEncrypt)
// Test GBK String
enc := charset.FindEncoding("gbk")
gbkStr, _ := enc.Transform(nil, []byte("你好"), charset.OpEncode)
gbkTests := []struct {
mode string
chs string
origin any
params []any
crypt string
}{
// test for ecb
{"aes-128-ecb", "utf8mb4", "你好", []any{"123"}, "CEBD80EEC6423BEAFA1BB30FD7625CBC"},
{"aes-128-ecb", "gbk", gbkStr, []any{"123"}, "6AFA9D7BA2C1AED1603E804F75BB0127"},
{"aes-128-ecb", "utf8mb4", "123", []any{"你好"}, "E03F6D9C1C86B82F5620EE0AA9BD2F6A"},
{"aes-128-ecb", "gbk", "123", []any{"你好"}, "31A2D26529F0E6A38D406379ABD26FA5"},
{"aes-128-ecb", "utf8mb4", "你好", []any{"你好"}, "3E2D8211DAE17143F22C2C5969A35263"},
{"aes-128-ecb", "gbk", gbkStr, []any{"你好"}, "84982910338160D037615D283AD413DE"},
// test for cbc
{"aes-128-cbc", "utf8mb4", "你好", []any{"123", "1234567890123456"}, "B95509A516ACED59C3DF4EC41C538D83"},
{"aes-128-cbc", "gbk", gbkStr, []any{"123", "1234567890123456"}, "D4322D091B5DDE0DEB35B1749DA2483C"},
{"aes-128-cbc", "utf8mb4", "123", []any{"你好", "1234567890123456"}, "E19E86A9E78E523267AFF36261AD117D"},
{"aes-128-cbc", "gbk", "123", []any{"你好", "1234567890123456"}, "5A2F8F2C1841CC4E1D1640F1EA2A1A23"},
{"aes-128-cbc", "utf8mb4", "你好", []any{"你好", "1234567890123456"}, "B73637C73302C909EA63274C07883E71"},
{"aes-128-cbc", "gbk", gbkStr, []any{"你好", "1234567890123456"}, "61E13E9B00F2E757F4E925D3268227A0"},
}
for _, tt := range gbkTests {
msg := fmt.Sprintf("%v", tt)
err := ctx.GetSessionVars().SetSystemVar(vardef.CharacterSetConnection, tt.chs)
require.NoError(t, err, msg)
err = ctx.GetSessionVars().SetSystemVar(vardef.BlockEncryptionMode, tt.mode)
require.NoError(t, err, msg)
args := primitiveValsToConstants(ctx, []any{tt.origin})
args = append(args, primitiveValsToConstants(ctx, tt.params)...)
f, err := fc.getFunction(ctx, args)
require.NoError(t, err, msg)
crypt, err := evalBuiltinFunc(f, ctx, chunk.Row{})
require.NoError(t, err, msg)
require.Equal(t, types.NewDatum(tt.crypt), toHex(crypt), msg)
}
}
func TestAESDecrypt(t *testing.T) {
ctx := createContext(t)
fc := funcs[ast.AesDecrypt]
for _, tt := range aesTests {
msg := fmt.Sprintf("%v", tt)
err := ctx.GetSessionVars().SetSystemVar(vardef.BlockEncryptionMode, tt.mode)
require.NoError(t, err, msg)
args := []types.Datum{fromHex(tt.crypt)}
for _, param := range tt.params {
args = append(args, types.NewDatum(param))
}
f, err := fc.getFunction(ctx, datumsToConstants(args))
require.NoError(t, err, msg)
str, err := evalBuiltinFunc(f, ctx, chunk.Row{})
require.NoError(t, err, msg)
if tt.origin == nil {
require.True(t, str.IsNull())
continue
}
require.Equal(t, types.NewCollationStringDatum(tt.origin.(string), charset.CollationBin), str, msg)
}
err := ctx.GetSessionVars().SetSystemVar(vardef.BlockEncryptionMode, "aes-128-ecb")
require.NoError(t, err)
testNullInput(t, ctx, ast.AesDecrypt)
testAmbiguousInput(t, ctx, ast.AesDecrypt)
// Test GBK String
enc := charset.FindEncoding("gbk")
r, _ := enc.Transform(nil, []byte("你好"), charset.OpEncode)
gbkStr := string(r)
gbkTests := []struct {
mode string
chs string
origin any
params []any
crypt string
}{
// test for ecb
{"aes-128-ecb", "utf8mb4", "你好", []any{"123"}, "CEBD80EEC6423BEAFA1BB30FD7625CBC"},
{"aes-128-ecb", "gbk", gbkStr, []any{"123"}, "6AFA9D7BA2C1AED1603E804F75BB0127"},
{"aes-128-ecb", "utf8mb4", "123", []any{"你好"}, "E03F6D9C1C86B82F5620EE0AA9BD2F6A"},
{"aes-128-ecb", "gbk", "123", []any{"你好"}, "31A2D26529F0E6A38D406379ABD26FA5"},
{"aes-128-ecb", "utf8mb4", "你好", []any{"你好"}, "3E2D8211DAE17143F22C2C5969A35263"},
{"aes-128-ecb", "gbk", gbkStr, []any{"你好"}, "84982910338160D037615D283AD413DE"},
// test for cbc
{"aes-128-cbc", "utf8mb4", "你好", []any{"123", "1234567890123456"}, "B95509A516ACED59C3DF4EC41C538D83"},
{"aes-128-cbc", "gbk", gbkStr, []any{"123", "1234567890123456"}, "D4322D091B5DDE0DEB35B1749DA2483C"},
{"aes-128-cbc", "utf8mb4", "123", []any{"你好", "1234567890123456"}, "E19E86A9E78E523267AFF36261AD117D"},
{"aes-128-cbc", "gbk", "123", []any{"你好", "1234567890123456"}, "5A2F8F2C1841CC4E1D1640F1EA2A1A23"},
{"aes-128-cbc", "utf8mb4", "你好", []any{"你好", "1234567890123456"}, "B73637C73302C909EA63274C07883E71"},
{"aes-128-cbc", "gbk", gbkStr, []any{"你好", "1234567890123456"}, "61E13E9B00F2E757F4E925D3268227A0"},
}
for _, tt := range gbkTests {
msg := fmt.Sprintf("%v", tt)
err := ctx.GetSessionVars().SetSystemVar(vardef.CharacterSetConnection, tt.chs)
require.NoError(t, err, msg)
err = ctx.GetSessionVars().SetSystemVar(vardef.BlockEncryptionMode, tt.mode)
require.NoError(t, err, msg)
// Set charset and collate except first argument
args := datumsToConstants([]types.Datum{fromHex(tt.crypt)})
args = append(args, primitiveValsToConstants(ctx, tt.params)...)
f, err := fc.getFunction(ctx, args)
require.NoError(t, err, msg)
str, err := evalBuiltinFunc(f, ctx, chunk.Row{})
require.NoError(t, err, msg)
require.Equal(t, types.NewCollationStringDatum(tt.origin.(string), charset.CollationBin), str, msg)
}
}
func testNullInput(t *testing.T, ctx *mock.Context, fnName string) {
err := ctx.GetSessionVars().SetSystemVar(vardef.BlockEncryptionMode, "aes-128-ecb")
require.NoError(t, err)
fc := funcs[fnName]
arg := types.NewStringDatum("str")
var argNull types.Datum
f, err := fc.getFunction(ctx, datumsToConstants([]types.Datum{arg, argNull}))
require.NoError(t, err)
crypt, err := evalBuiltinFunc(f, ctx, chunk.Row{})
require.NoError(t, err)
require.True(t, crypt.IsNull())
f, err = fc.getFunction(ctx, datumsToConstants([]types.Datum{argNull, arg}))
require.NoError(t, err)
crypt, err = evalBuiltinFunc(f, ctx, chunk.Row{})
require.NoError(t, err)
require.True(t, crypt.IsNull())
}
func testAmbiguousInput(t *testing.T, ctx *mock.Context, fnName string) {
fc := funcs[fnName]
arg := types.NewStringDatum("str")
// test for modes that require init_vector
err := ctx.GetSessionVars().SetSystemVar(vardef.BlockEncryptionMode, "aes-128-cbc")
require.NoError(t, err)
_, err = fc.getFunction(ctx, datumsToConstants([]types.Datum{arg, arg}))
require.Error(t, err)
f, err := fc.getFunction(ctx, datumsToConstants([]types.Datum{arg, arg, types.NewStringDatum("iv < 16 bytes")}))
require.NoError(t, err)
_, err = evalBuiltinFunc(f, ctx, chunk.Row{})
require.Error(t, err)
// test for modes that do not require init_vector
err = ctx.GetSessionVars().SetSystemVar(vardef.BlockEncryptionMode, "aes-128-ecb")
require.NoError(t, err)
f, err = fc.getFunction(ctx, datumsToConstants([]types.Datum{arg, arg, arg}))
require.NoError(t, err)
_, err = evalBuiltinFunc(f, ctx, chunk.Row{})
require.NoError(t, err)
warnings := ctx.GetSessionVars().StmtCtx.GetWarnings()
require.GreaterOrEqual(t, len(warnings), 1)
}
func toHex(d types.Datum) (h types.Datum) {
if d.IsNull() {
return
}
x, _ := d.ToString()
h.SetString(strings.ToUpper(hex.EncodeToString(hack.Slice(x))), mysql.DefaultCollationName)
return
}
func fromHex(str any) (d types.Datum) {
if str == nil {
return
}
if s, ok := str.(string); ok {
h, _ := hex.DecodeString(s)
d.SetBytes(h)
}
return d
}
func TestSha1Hash(t *testing.T) {
ctx := createContext(t)
sha1Tests := []struct {
chs string
origin any
crypt string
}{
{mysql.DefaultCollationName, "test", "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"},
{mysql.DefaultCollationName, "c4pt0r", "034923dcabf099fc4c8917c0ab91ffcd4c2578a6"},
{mysql.DefaultCollationName, "pingcap", "73bf9ef43a44f42e2ea2894d62f0917af149a006"},
{mysql.DefaultCollationName, "foobar", "8843d7f92416211de9ebb963ff4ce28125932878"},
{mysql.DefaultCollationName, 1024, "128351137a9c47206c4507dcf2e6fbeeca3a9079"},
{mysql.DefaultCollationName, 123.45, "22f8b438ad7e89300b51d88684f3f0b9fa1d7a32"},
{"gbk", 123.45, "22f8b438ad7e89300b51d88684f3f0b9fa1d7a32"},
{"gbk", "一二三", "30cda4eed59a2ff592f2881f39d42fed6e10cad8"},
{"gbk", "一二三123", "1e24acbf708cd889c1d5be90abc1f14eaf14d0b4"},
{"gbk", "", "da39a3ee5e6b4b0d3255bfef95601890afd80709"},
}
fc := funcs[ast.SHA]
for _, tt := range sha1Tests {
err := ctx.GetSessionVars().SetSystemVarWithoutValidation(vardef.CharacterSetConnection, tt.chs)
require.NoError(t, err)
f, _ := fc.getFunction(ctx, primitiveValsToConstants(ctx, []any{tt.origin}))
crypt, err := evalBuiltinFunc(f, ctx, chunk.Row{})
require.NoError(t, err)
res, err := crypt.ToString()
require.NoError(t, err)
require.Equal(t, tt.crypt, res)
}
// test NULL input for sha
var argNull types.Datum
f, _ := fc.getFunction(ctx, datumsToConstants([]types.Datum{argNull}))
crypt, err := evalBuiltinFunc(f, ctx, chunk.Row{})
require.NoError(t, err)
require.True(t, crypt.IsNull())
}
func TestSha2Hash(t *testing.T) {
ctx := createContext(t)
sha2Tests := []struct {
chs string
origin any
hashLength any
crypt any
validCase bool
}{
{mysql.DefaultCollationName, "pingcap", 0, "2871823be240f8ecd1d72f24c99eaa2e58af18b4b8ba99a4fc2823ba5c43930a", true},
{mysql.DefaultCollationName, "pingcap", 224, "cd036dc9bec69e758401379c522454ea24a6327b48724b449b40c6b7", true},
{mysql.DefaultCollationName, "pingcap", 256, "2871823be240f8ecd1d72f24c99eaa2e58af18b4b8ba99a4fc2823ba5c43930a", true},
{mysql.DefaultCollationName, "pingcap", 384, "c50955b6b0c7b9919740d956849eedcb0f0f90bf8a34e8c1f4e071e3773f53bd6f8f16c04425ff728bed04de1b63db51", true},
{mysql.DefaultCollationName, "pingcap", 512, "ea903c574370774c4844a83b7122105a106e04211673810e1baae7c2ae7aba2cf07465e02f6c413126111ef74a417232683ce7ba210052e63c15fc82204aad80", true},
{mysql.DefaultCollationName, 13572468, 0, "1c91ab1c162fd0cae60a5bb9880f3e7d5a133a65b6057a644b26973d9c55dcfe", true},
{mysql.DefaultCollationName, 13572468, 224, "8ad67735bbf49576219f364f4640d595357a440358d15bf6815a16e4", true},
{mysql.DefaultCollationName, 13572468, 256, "1c91ab1c162fd0cae60a5bb9880f3e7d5a133a65b6057a644b26973d9c55dcfe", true},
{mysql.DefaultCollationName, 13572468.123, 384, "3b4ee302435dc1e15251efd9f3982b1ca6fe4ac778d3260b7bbf3bea613849677eda830239420e448e4c6dc7c2649d89", true},
{mysql.DefaultCollationName, 13572468.123, 512, "4820aa3f2760836557dc1f2d44a0ba7596333fdb60c8a1909481862f4ab0921c00abb23d57b7e67a970363cc3fcb78b25b6a0d45cdcac0e87aa0c96bc51f7f96", true},
{mysql.DefaultCollationName, nil, 224, nil, false},
{mysql.DefaultCollationName, "pingcap", nil, nil, false},
{mysql.DefaultCollationName, "pingcap", 123, nil, false},
{"gbk", "pingcap", 0, "2871823be240f8ecd1d72f24c99eaa2e58af18b4b8ba99a4fc2823ba5c43930a", true},
{"gbk", "pingcap", 224, "cd036dc9bec69e758401379c522454ea24a6327b48724b449b40c6b7", true},
{"gbk", "pingcap", 256, "2871823be240f8ecd1d72f24c99eaa2e58af18b4b8ba99a4fc2823ba5c43930a", true},
{"gbk", "pingcap", 384, "c50955b6b0c7b9919740d956849eedcb0f0f90bf8a34e8c1f4e071e3773f53bd6f8f16c04425ff728bed04de1b63db51", true},
{"gbk", "pingcap", 512, "ea903c574370774c4844a83b7122105a106e04211673810e1baae7c2ae7aba2cf07465e02f6c413126111ef74a417232683ce7ba210052e63c15fc82204aad80", true},
{"gbk", 13572468, 0, "1c91ab1c162fd0cae60a5bb9880f3e7d5a133a65b6057a644b26973d9c55dcfe", true},
{"gbk", 13572468, 224, "8ad67735bbf49576219f364f4640d595357a440358d15bf6815a16e4", true},
{"gbk", 13572468, 256, "1c91ab1c162fd0cae60a5bb9880f3e7d5a133a65b6057a644b26973d9c55dcfe", true},
{"gbk", 13572468.123, 384, "3b4ee302435dc1e15251efd9f3982b1ca6fe4ac778d3260b7bbf3bea613849677eda830239420e448e4c6dc7c2649d89", true},
{"gbk", 13572468.123, 512, "4820aa3f2760836557dc1f2d44a0ba7596333fdb60c8a1909481862f4ab0921c00abb23d57b7e67a970363cc3fcb78b25b6a0d45cdcac0e87aa0c96bc51f7f96", true},
{"gbk", nil, 224, nil, false},
{"gbk", "pingcap", nil, nil, false},
{"gbk", "pingcap", 123, nil, false},
{"gbk", "一二三", 0, "b6c1ae1f8d8a07426ddb13fca5124fb0b9f1f0ef1cca6730615099cf198ca8af", true},
{"gbk", "一二三", 224, "2362f577783f6cd6cc10b0308f946f479fef868a39d6339b5d74cc6d", true},
{"gbk", "一二三", 256, "b6c1ae1f8d8a07426ddb13fca5124fb0b9f1f0ef1cca6730615099cf198ca8af", true},
{"gbk", "一二三", 384, "54e75070f1faab03e7ce808ca2824ed4614ad1d58ee1409d8c1e4fd72ecab12c92ac3a2f919721c2aa09b23e5f3cc8aa", true},
{"gbk", "一二三", 512, "54fae3d0bb68bb4645af4a97a01fee1a6e3ecf7850f1ba41a994a46d23b60082262d00d9c635ff7ed02203e4806794dfa57c3654b3a4549bfb77ef1ddeab0224", true},
{"gbk", "一二三123", 0, "de059637dd572c2e21df1dd6d04512ad3a34f71964f14338e966356a091c0e7e", true},
{"gbk", "一二三123", 224, "a192909220fea1b74bcea87740f7550a2c03cf4f92d4c78ccedc9e3f", true},
{"gbk", "一二三123", 256, "de059637dd572c2e21df1dd6d04512ad3a34f71964f14338e966356a091c0e7e", true},
{"gbk", "一二三123", 384, "a487131f07fd46f66d7300be3c10bdae255e3296334a239240b28d32f038983331b276bd717363673e54733b594e7781", true},
{"gbk", "一二三123", 512, "9336a0844a5a1dc656d02ded28bf768cef9c39b47bd7292c75fc0d27fcb509ca765d24d502e5906e8afe1803fd5ea325e3d855a0206df6bc08fef5e7e34b0082", true},
{"gbk", "", 0, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", true},
{"gbk", "", 224, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", true},
{"gbk", "", 256, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", true},
{"gbk", "", 384, "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", true},
{"gbk", "", 512, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", true},
}
fc := funcs[ast.SHA2]
for _, tt := range sha2Tests {
err := ctx.GetSessionVars().SetSystemVarWithoutValidation(vardef.CharacterSetConnection, tt.chs)
require.NoError(t, err)
f, err := fc.getFunction(ctx, primitiveValsToConstants(ctx, []any{tt.origin, tt.hashLength}))
require.NoError(t, err)
crypt, err := evalBuiltinFunc(f, ctx, chunk.Row{})
require.NoError(t, err)
if tt.validCase {
res, err := crypt.ToString()
require.NoError(t, err)
require.Equal(t, tt.crypt, res)
} else {
require.True(t, crypt.IsNull())
}
}
}
func TestMD5Hash(t *testing.T) {
ctx := createContext(t)
cases := []struct {
args any
expected string
charset string
isNil bool
getErr bool
}{
{"", "d41d8cd98f00b204e9800998ecf8427e", "", false, false},
{"a", "0cc175b9c0f1b6a831c399e269772661", "", false, false},
{"ab", "187ef4436122d1cc2f40dc2b92f0eba0", "", false, false},
{"abc", "900150983cd24fb0d6963f7d28e17f72", "", false, false},
{"abc", "900150983cd24fb0d6963f7d28e17f72", "gbk", false, false},
{123, "202cb962ac59075b964b07152d234b70", "", false, false},
{"123", "202cb962ac59075b964b07152d234b70", "", false, false},
{"123", "202cb962ac59075b964b07152d234b70", "gbk", false, false},
{123.123, "46ddc40585caa8abc07c460b3485781e", "", false, false},
{"一二三", "8093a32450075324682d01456d6e3919", "", false, false},
{"一二三", "a45d4af7b243e7f393fa09bed72ac73e", "gbk", false, false},
{"ㅂ123", "0e85d0f68c104b65a15d727e26705596", "", false, false},
{"ㅂ123", "", "gbk", false, true},
{nil, "", "", true, false},
}
for _, c := range cases {
err := ctx.GetSessionVars().SetSystemVarWithoutValidation(vardef.CharacterSetConnection, c.charset)
require.NoError(t, err)
f, err := newFunctionForTest(ctx, ast.MD5, primitiveValsToConstants(ctx, []any{c.args})...)
require.NoError(t, err)
d, err := f.Eval(ctx, chunk.Row{})
if c.getErr {
require.Error(t, err)
} else {
require.NoError(t, err)
if c.isNil {
require.Equal(t, types.KindNull, d.Kind())
} else {
require.Equal(t, c.expected, d.GetString())
}
}
}
_, err := funcs[ast.MD5].getFunction(ctx, []Expression{NewZero()})
require.NoError(t, err)
}
func MD5HashOld(arg string) string {
sum := md5.Sum([]byte(arg))
return fmt.Sprintf("%x", sum)
}
func MD5HashNew(arg string) string {
sum := md5.Sum([]byte(arg))
return hex.EncodeToString(sum[:])
}
func BenchmarkMD5Hash(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
MD5HashOld("abc")
//MD5HashNew("abc")
}
}
func TestRandomBytes(t *testing.T) {
ctx := createContext(t)
fc := funcs[ast.RandomBytes]
f, err := fc.getFunction(ctx, datumsToConstants([]types.Datum{types.NewDatum(32)}))
require.NoError(t, err)
out, err := evalBuiltinFunc(f, ctx, chunk.Row{})
require.NoError(t, err)
require.Equal(t, 32, len(out.GetBytes()))
f, err = fc.getFunction(ctx, datumsToConstants([]types.Datum{types.NewDatum(1025)}))
require.NoError(t, err)
_, err = evalBuiltinFunc(f, ctx, chunk.Row{})
require.Error(t, err)
f, err = fc.getFunction(ctx, datumsToConstants([]types.Datum{types.NewDatum(-32)}))
require.NoError(t, err)
_, err = evalBuiltinFunc(f, ctx, chunk.Row{})
require.Error(t, err)
f, err = fc.getFunction(ctx, datumsToConstants([]types.Datum{types.NewDatum(0)}))
require.NoError(t, err)
_, err = evalBuiltinFunc(f, ctx, chunk.Row{})
require.Error(t, err)
f, err = fc.getFunction(ctx, datumsToConstants([]types.Datum{types.NewDatum(nil)}))
require.NoError(t, err)
out, err = evalBuiltinFunc(f, ctx, chunk.Row{})
require.NoError(t, err)
require.Equal(t, 0, len(out.GetBytes()))
}
func decodeHex(str string) []byte {
ret, err := hex.DecodeString(str)
if err != nil {
panic(err)
}
return ret
}
func TestCompress(t *testing.T) {
ctx := createContext(t)
fc := funcs[ast.Compress]
tests := []struct {
chs string
in any
expect any
}{
{"", "hello world", string(decodeHex("0B000000789CCA48CDC9C95728CF2FCA4901040000FFFF1A0B045D"))},
{"", "", ""},
{"", nil, nil},
{"utf8mb4", "hello world", string(decodeHex("0B000000789CCA48CDC9C95728CF2FCA4901040000FFFF1A0B045D"))},
{"gbk", "hello world", string(decodeHex("0B000000789CCA48CDC9C95728CF2FCA4901040000FFFF1A0B045D"))},
{"utf8mb4", "你好", string(decodeHex("06000000789C7AB277C1D3A57B01010000FFFF10450489"))},
{"gbk", "你好", string(decodeHex("04000000789C3AF278D76140000000FFFF07F40325"))},
}
for _, test := range tests {
err := ctx.GetSessionVars().SetSystemVarWithoutValidation(vardef.CharacterSetConnection, test.chs)
require.NoErrorf(t, err, "%v", test)
arg := primitiveValsToConstants(ctx, []any{test.in})
f, err := fc.getFunction(ctx, arg)
require.NoErrorf(t, err, "%v", test)
out, err := evalBuiltinFunc(f, ctx, chunk.Row{})
require.NoErrorf(t, err, "%v", test)
if test.expect == nil {
require.Truef(t, out.IsNull(), "%v", test)
continue
}
require.Equalf(t, types.NewCollationStringDatum(test.expect.(string), charset.CollationBin), out, "%v", test)
}
}
func TestUncompress(t *testing.T) {
ctx := createContext(t)
tests := []struct {
in any
expect any
}{
{decodeHex("0B000000789CCB48CDC9C95728CF2FCA4901001A0B045D"), "hello world"}, // zlib result from MySQL
{decodeHex("0B000000789CCA48CDC9C95728CF2FCA4901040000FFFF1A0B045D"), "hello world"}, // zlib result from TiDB
{decodeHex("02000000789CCB48CDC9C95728CF2FCA4901001A0B045D"), nil}, // wrong length in the first four bytes
{decodeHex(""), ""},
{"1", nil},
{"1234", nil},
{"12345", nil},
{decodeHex("0B"), nil},
{decodeHex("0B000000"), nil},
{decodeHex("0B0000001234"), nil},
{12345, nil},
{nil, nil},
}
fc := funcs[ast.Uncompress]
for _, test := range tests {
arg := types.NewDatum(test.in)
f, err := fc.getFunction(ctx, datumsToConstants([]types.Datum{arg}))
require.NoErrorf(t, err, "%v", test)
out, err := evalBuiltinFunc(f, ctx, chunk.Row{})
require.NoErrorf(t, err, "%v", test)
if test.expect == nil {
require.Truef(t, out.IsNull(), "%v", test)
continue
}
require.Equalf(t, types.NewCollationStringDatum(test.expect.(string), charset.CollationBin), out, "%v", test)
}
}
func TestUncompressLength(t *testing.T) {
ctx := createContext(t)
tests := []struct {
in any
expect any
}{
{decodeHex("0B000000789CCB48CDC9C95728CF2FCA4901001A0B045D"), int64(11)}, // zlib result from MySQL
{decodeHex("0B000000789CCA48CDC9C95728CF2FCA4901040000FFFF1A0B045D"), int64(11)}, // zlib result from TiDB
{decodeHex(""), int64(0)},
{"1", int64(0)},
{"123", int64(0)},
{decodeHex("0B"), int64(0)},
{decodeHex("0B00"), int64(0)},
{decodeHex("0B000000"), int64(0x0)},
{decodeHex("0B0000001234"), int64(0x0B)},
{12345, int64(875770417)},
{nil, nil},
}
fc := funcs[ast.UncompressedLength]
for _, test := range tests {
arg := types.NewDatum(test.in)
f, err := fc.getFunction(ctx, datumsToConstants([]types.Datum{arg}))
require.NoErrorf(t, err, "%v", test)
out, err := evalBuiltinFunc(f, ctx, chunk.Row{})
require.NoErrorf(t, err, "%v", test)
require.Equalf(t, types.NewDatum(test.expect), out, "%v", test)
}
}
func TestValidatePasswordStrength(t *testing.T) {
ctx := createContext(t)
ctx.GetSessionVars().User = &auth.UserIdentity{Username: "testuser"}
globalVarsAccessor := variable.NewMockGlobalAccessor4Tests()
ctx.GetSessionVars().GlobalVarsAccessor = globalVarsAccessor
err := globalVarsAccessor.SetGlobalSysVar(context.Background(), vardef.ValidatePasswordDictionary, "1234")
require.NoError(t, err)
tests := []struct {
in any
expect any
}{
{nil, nil},
{"123", 0},
{"testuser123", 0},
{"resutset123", 0},
{"12345", 25},
{"12345678", 50},
{"!Abc12345678", 75},
{"!Abc87654321", 100},
}
fc := funcs[ast.ValidatePasswordStrength]
// disable password validation
for _, test := range tests {
arg := types.NewDatum(test.in)
f, err := fc.getFunction(ctx, datumsToConstants([]types.Datum{arg}))
require.NoErrorf(t, err, "%v", test)
out, err := evalBuiltinFunc(f, ctx, chunk.Row{})
require.NoErrorf(t, err, "%v", test)
if test.expect == nil {
require.Equal(t, types.NewDatum(nil), out)
} else {
require.Equalf(t, types.NewDatum(0), out, "%v", test)
}
}
// enable password validation
err = globalVarsAccessor.SetGlobalSysVar(context.Background(), vardef.ValidatePasswordEnable, "ON")
require.NoError(t, err)
for _, test := range tests {
arg := types.NewDatum(test.in)
f, err := fc.getFunction(ctx, datumsToConstants([]types.Datum{arg}))
require.NoErrorf(t, err, "%v", test)
out, err := evalBuiltinFunc(f, ctx, chunk.Row{})
require.NoErrorf(t, err, "%v", test)
require.Equalf(t, types.NewDatum(test.expect), out, "%v", test)
}
}
func TestPassword(t *testing.T) {
ctx := createContext(t)
cases := []struct {
args any
expected string
charset string
isNil bool
getErr bool
getWarn bool
}{
{nil, "", "", false, false, false},
{"", "", "", false, false, false},
{"abc", "*0D3CED9BEC10A777AEC23CCC353A8C08A633045E", "", false, false, true},
{"abc", "*0D3CED9BEC10A777AEC23CCC353A8C08A633045E", "gbk", false, false, true},
{123, "*23AE809DDACAF96AF0FD78ED04B6A265E05AA257", "", false, false, true},
{1.23, "*A589EEBA8D3F9E1A34A7EE518FAC4566BFAD5BB6", "", false, false, true},
{"一二三四", "*D207780722F22B23C254CAC0580D3B6738C19E18", "", false, false, true},
{"一二三四", "*48E0460AD45CF66AC6B8C18CB8B4BC8A403D935B", "gbk", false, false, true},
{"ㅂ123", "", "gbk", false, true, false},
{types.NewDecFromFloatForTest(123.123), "*B15B84262DB34BFB2C817A45A55C405DC7C52BB1", "", false, false, true},
}
warnCount := len(ctx.GetSessionVars().StmtCtx.GetWarnings())
for _, c := range cases {
err := ctx.GetSessionVars().SetSystemVarWithoutValidation(vardef.CharacterSetConnection, c.charset)
require.NoError(t, err)
f, err := newFunctionForTest(ctx, ast.PasswordFunc, primitiveValsToConstants(ctx, []any{c.args})...)
require.NoError(t, err)
d, err := f.Eval(ctx, chunk.Row{})
if c.getErr {
require.Error(t, err)
continue
}
require.NoError(t, err)
if c.isNil {
require.Equal(t, types.KindNull, d.Kind())
} else {
require.Equal(t, c.expected, d.GetString())
}
warnings := ctx.GetSessionVars().StmtCtx.GetWarnings()
if c.getWarn {
require.Equal(t, warnCount+1, len(warnings))
lastWarn := warnings[len(warnings)-1]
require.Truef(t, terror.ErrorEqual(errDeprecatedSyntaxNoReplacement, lastWarn.Err), "err %v", lastWarn.Err)
warnCount = len(warnings)
} else {
require.Equal(t, warnCount, len(warnings))
}
}
_, err := funcs[ast.PasswordFunc].getFunction(ctx, []Expression{NewZero()})
require.NoError(t, err)
}