Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

expression: ECB/CBC modes with 128/192/256-bit key length for AES #7425

Merged
merged 23 commits into from
Sep 23, 2018
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 101 additions & 13 deletions expression/builtin_encryption.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,16 @@ import (
"hash"
"io"

"crypto/aes"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please put the standard packages together and add an empty line between the third-part packages.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

"github.com/juju/errors"
"github.com/pingcap/tidb/mysql"
"github.com/pingcap/tidb/sessionctx"
"github.com/pingcap/tidb/sessionctx/variable"
"github.com/pingcap/tidb/types"
"github.com/pingcap/tidb/util/auth"
"github.com/pingcap/tidb/util/chunk"
"github.com/pingcap/tidb/util/encrypt"
"strings"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please move strings to line 28(it's TiDB rule, standard package need in first import group ^ ^)

)

var (
Expand Down Expand Up @@ -68,10 +71,21 @@ var (
_ builtinFunc = &builtinUncompressedLengthSig{}
)

// TODO: support other mode
const (
aes128ecbBlobkSize = 16
)
type aesModeAttr struct {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to add some comment about this struct.

modeName string
keySize int
ivRequired bool
}

var aesModes = map[string]*aesModeAttr{
//TODO support more modes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we create a github issue for this TODO and explain what modes need to be supported?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. see #7490

"aes-128-ecb": {"ecb", 16, false},
"aes-192-ecb": {"ecb", 24, false},
"aes-256-ecb": {"ecb", 32, false},
"aes-128-cbc": {"cbc", 16, true},
"aes-192-cbc": {"cbc", 24, true},
"aes-256-cbc": {"cbc", 32, true},
}

type aesDecryptFunctionClass struct {
baseFunctionClass
Expand All @@ -81,7 +95,11 @@ func (c *aesDecryptFunctionClass) getFunction(ctx sessionctx.Context, args []Exp
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(c.verifyArgs(args))
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString, types.ETString)
argTps := make([]types.EvalType, 0, len(args))
for range args {
argTps = append(argTps, types.ETString)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, argTps...)
bf.tp.Flen = args[0].GetType().Flen // At most.
types.SetBinChsClnFlag(bf.tp)
sig := &builtinAesDecryptSig{bf}
Expand Down Expand Up @@ -112,9 +130,42 @@ func (b *builtinAesDecryptSig) evalString(row chunk.Row) (string, bool, error) {
return "", true, errors.Trace(err)
}

// TODO: Support other modes.
key := encrypt.DeriveKeyMySQL([]byte(keyStr), aes128ecbBlobkSize)
plainText, err := encrypt.AESDecryptWithECB([]byte(cryptStr), key)
var iv string
modeName, _ := b.ctx.GetSessionVars().GetSystemVar(variable.BlockEncryptionMode)
mode, exists := aesModes[strings.ToLower(modeName)]
if !exists {
return "", true, errors.Errorf("unsupported block encryption mode - %v", modeName)
}
if mode.ivRequired {
if len(b.args) != 3 {
return "", true, ErrIncorrectParameterCount.GenByArgs("aes_decrypt")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check should be done in aesDecryptFunctionClass.getFunction()

}
iv, isNull, err = b.args[2].EvalString(b.ctx, row)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
if len(iv) < aes.BlockSize {
Copy link
Contributor

@lysu lysu Aug 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about give another const IVSize to help understand~? although our IVSize = BlockSize = 16

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. add const IVSize = aes.BlockSize.

return "", true, errIncorrectArgs.Gen("The initialization vector supplied to aes_decrypt is too short. Must be at least 16 bytes long")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this error message the same with MySQL?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes..I had checked, messages is same to mysql.

but in strict, errorcode returned by this PR has some different to mysql, expect this point, new code using errors.Errorf maybe should add a new ErrXXX.GenByArgs

}
// init_vector must be 16 bytes or longer (bytes in excess of 16 are ignored)
iv = iv[0:aes.BlockSize]
} else {
if len(b.args) == 3 {
// For modes that do not require init_vector, it is ignored and a warning is generated if it is specified.
b.ctx.GetSessionVars().StmtCtx.AppendWarning(errWarnOptionIgnored.GenByArgs("IV"))
}
}

key := encrypt.DeriveKeyMySQL([]byte(keyStr), mode.keySize)
var plainText []byte
switch mode.modeName {
case "ecb":
plainText, err = encrypt.AESDecryptWithECB([]byte(cryptStr), key)
case "cbc":
plainText, err = encrypt.AESDecryptWithCBC([]byte(cryptStr), key, []byte(iv))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems some duplicate logic in AESDecryptWithECB and AESDecryptWithCBC, AESEncryptWithECB and AESEncryptWithCBC~

what about use AESDecrypt, AESEncrypt, then inversion of control, pass a cipher.BlockMode argument?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I add "type blockModeBuild func(block cipher.Block) cipher.BlockMode" to create BlockMode argument.

default:
return "", true, errors.Errorf("unsupported block encryption mode - %v", mode.modeName)
}
if err != nil {
return "", true, nil
}
Expand All @@ -129,8 +180,12 @@ func (c *aesEncryptFunctionClass) getFunction(ctx sessionctx.Context, args []Exp
if err := c.verifyArgs(args); err != nil {
return nil, errors.Trace(c.verifyArgs(args))
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, types.ETString, types.ETString)
bf.tp.Flen = aes128ecbBlobkSize * (args[0].GetType().Flen/aes128ecbBlobkSize + 1) // At most.
argTps := make([]types.EvalType, 0, len(args))
for range args {
argTps = append(argTps, types.ETString)
}
bf := newBaseBuiltinFuncWithTp(ctx, args, types.ETString, argTps...)
bf.tp.Flen = aes.BlockSize * (args[0].GetType().Flen/aes.BlockSize + 1) // At most.
types.SetBinChsClnFlag(bf.tp)
sig := &builtinAesEncryptSig{bf}
return sig, nil
Expand Down Expand Up @@ -160,9 +215,42 @@ func (b *builtinAesEncryptSig) evalString(row chunk.Row) (string, bool, error) {
return "", true, errors.Trace(err)
}

// TODO: Support other modes.
key := encrypt.DeriveKeyMySQL([]byte(keyStr), aes128ecbBlobkSize)
cipherText, err := encrypt.AESEncryptWithECB([]byte(str), key)
var iv string
modeName, _ := b.ctx.GetSessionVars().GetSystemVar(variable.BlockEncryptionMode)
mode, exists := aesModes[strings.ToLower(modeName)]
if !exists {
return "", true, errors.Errorf("unsupported block encryption mode - %v", modeName)
}
if mode.ivRequired {
if len(b.args) != 3 {
return "", true, ErrIncorrectParameterCount.GenByArgs("aes_encrypt")
}
iv, isNull, err = b.args[2].EvalString(b.ctx, row)
if isNull || err != nil {
return "", true, errors.Trace(err)
}
if len(iv) < aes.BlockSize {
return "", true, errIncorrectArgs.Gen("The initialization vector supplied to aes_encrypt is too short. Must be at least 16 bytes long")
}
// init_vector must be 16 bytes or longer (bytes in excess of 16 are ignored)
iv = iv[0:aes.BlockSize]
} else {
if len(b.args) == 3 {
// For modes that do not require init_vector, it is ignored and a warning is generated if it is specified.
b.ctx.GetSessionVars().StmtCtx.AppendWarning(errWarnOptionIgnored.GenByArgs("IV"))
}
}

key := encrypt.DeriveKeyMySQL([]byte(keyStr), mode.keySize)
var cipherText []byte
switch mode.modeName {
case "ecb":
cipherText, err = encrypt.AESEncryptWithECB([]byte(str), key)
case "cbc":
cipherText, err = encrypt.AESEncryptWithCBC([]byte(str), key, []byte(iv))
default:
return "", true, errors.Errorf("unsupported block encryption mode - %v", mode.modeName)
}
if err != nil {
return "", true, nil
}
Expand Down
70 changes: 56 additions & 14 deletions expression/builtin_encryption_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (

. "github.com/pingcap/check"
"github.com/pingcap/tidb/ast"
"github.com/pingcap/tidb/sessionctx/variable"
"github.com/pingcap/tidb/terror"
"github.com/pingcap/tidb/types"
"github.com/pingcap/tidb/util/chunk"
Expand All @@ -27,47 +28,71 @@ import (
)

var aesTests = []struct {
origin interface{}
key interface{}
crypt interface{}
mode string
origin interface{}
key interface{}
initVector interface{}
crypt interface{}
}{
{"pingcap", "1234567890123456", "697BFE9B3F8C2F289DD82C88C7BC95C4"},
{"pingcap123", "1234567890123456", "CEC348F4EF5F84D3AA6C4FA184C65766"},
{"pingcap", "123456789012345678901234", "6F1589686860C8E8C7A40A78B25FF2C0"},
{"pingcap", "123", "996E0CA8688D7AD20819B90B273E01C6"},
{"pingcap", 123, "996E0CA8688D7AD20819B90B273E01C6"},
{nil, 123, nil},
// test for ecb
{"aes-128-ecb", "pingcap", "1234567890123456", nil, "697BFE9B3F8C2F289DD82C88C7BC95C4"},
{"aes-128-ecb", "pingcap123", "1234567890123456", nil, "CEC348F4EF5F84D3AA6C4FA184C65766"},
{"aes-128-ecb", "pingcap", "123456789012345678901234", nil, "6F1589686860C8E8C7A40A78B25FF2C0"},
{"aes-128-ecb", "pingcap", "123", nil, "996E0CA8688D7AD20819B90B273E01C6"},
{"aes-128-ecb", "pingcap", 123, nil, "996E0CA8688D7AD20819B90B273E01C6"},
{"aes-128-ecb", nil, 123, nil, nil},
{"aes-192-ecb", "pingcap", "1234567890123456", nil, "9B139FD002E6496EA2D5C73A2265E661"},
{"aes-256-ecb", "pingcap", "1234567890123456", nil, "F80DCDEDDBE5663BDB68F74AEDDB8EE3"},
// test for cbc
{"aes-128-cbc", "pingcap", "1234567890123456", "1234567890123456", "2ECA0077C5EA5768A0485AA522774792"},
{"aes-128-cbc", "pingcap", "123456789012345678901234", "1234567890123456", "483788634DA8817423BA0934FD2C096E"},
{"aes-192-cbc", "pingcap", "1234567890123456", "1234567890123456", "516391DB38E908ECA93AAB22870EC787"},
{"aes-256-cbc", "pingcap", "1234567890123456", "1234567890123456", "5D0E22C1E77523AEF5C3E10B65653C8F"},
{"aes-256-cbc", "pingcap", "12345678901234561234567890123456", "1234567890123456", "A26BA27CA4BE9D361D545AA84A17002D"},
{"aes-256-cbc", "pingcap", "1234567890123456", "12345678901234561234567890123456", "5D0E22C1E77523AEF5C3E10B65653C8F"},
}

func (s *testEvaluatorSuite) TestAESEncrypt(c *C) {
defer testleak.AfterTest(c)()
fc := funcs[ast.AesEncrypt]
for _, tt := range aesTests {
variable.SetSessionSystemVar(s.ctx.GetSessionVars(), variable.BlockEncryptionMode, types.NewDatum(tt.mode))
str := types.NewDatum(tt.origin)
key := types.NewDatum(tt.key)
f, err := fc.getFunction(s.ctx, s.datumsToConstants([]types.Datum{str, key}))
args := []types.Datum{str, key}
if tt.initVector != nil {
vec := types.NewDatum(tt.initVector)
args = append(args, vec)
}
f, err := fc.getFunction(s.ctx, s.datumsToConstants(args))
crypt, err := evalBuiltinFunc(f, chunk.Row{})
c.Assert(err, IsNil)
c.Assert(toHex(crypt), DeepEquals, types.NewDatum(tt.crypt))
}
s.testNullInput(c, ast.AesDecrypt)
s.testAmbiguousInput(c, ast.AesEncrypt)
}

func (s *testEvaluatorSuite) TestAESDecrypt(c *C) {
defer testleak.AfterTest(c)()
fc := funcs[ast.AesDecrypt]
for _, test := range aesTests {
variable.SetSessionSystemVar(s.ctx.GetSessionVars(), variable.BlockEncryptionMode, types.NewDatum(test.mode))
cryptStr := fromHex(test.crypt)
key := types.NewDatum(test.key)
f, err := fc.getFunction(s.ctx, s.datumsToConstants([]types.Datum{cryptStr, key}))
args := []types.Datum{cryptStr, key}
if test.initVector != nil {
vec := types.NewDatum(test.initVector)
args = append(args, vec)
}
f, err := fc.getFunction(s.ctx, s.datumsToConstants(args))
str, err := evalBuiltinFunc(f, chunk.Row{})
c.Assert(err, IsNil)
c.Assert(str, DeepEquals, types.NewDatum(test.origin))
}
s.testNullInput(c, ast.AesDecrypt)
s.testAmbiguousInput(c, ast.AesDecrypt)
}

func (s *testEvaluatorSuite) testNullInput(c *C, fnName string) {
func (s *testEvaluatorSuite) testAmbiguousInput(c *C, fnName string) {
fc := funcs[fnName]
arg := types.NewStringDatum("str")
var argNull types.Datum
Expand All @@ -80,6 +105,23 @@ func (s *testEvaluatorSuite) testNullInput(c *C, fnName string) {
crypt, err = evalBuiltinFunc(f, chunk.Row{})
c.Assert(err, IsNil)
c.Assert(crypt.IsNull(), IsTrue)

// test for modes that require init_vector
variable.SetSessionSystemVar(s.ctx.GetSessionVars(), variable.BlockEncryptionMode, types.NewDatum("aes-128-cbc"))
f, err = fc.getFunction(s.ctx, s.datumsToConstants([]types.Datum{arg, arg}))
crypt, err = evalBuiltinFunc(f, chunk.Row{})
c.Assert(err, NotNil)
f, err = fc.getFunction(s.ctx, s.datumsToConstants([]types.Datum{arg, arg, types.NewStringDatum("iv < 16 bytes")}))
crypt, err = evalBuiltinFunc(f, chunk.Row{})
c.Assert(err, NotNil)

// test for modes that do not require init_vector
variable.SetSessionSystemVar(s.ctx.GetSessionVars(), variable.BlockEncryptionMode, types.NewDatum("aes-128-ecb"))
f, err = fc.getFunction(s.ctx, s.datumsToConstants([]types.Datum{arg, arg, arg}))
crypt, err = evalBuiltinFunc(f, chunk.Row{})
c.Assert(err, IsNil)
warnings := s.ctx.GetSessionVars().StmtCtx.GetWarnings()
c.Assert(len(warnings), GreaterEqual, 1)
}

func toHex(d types.Datum) (h types.Datum) {
Expand Down
1 change: 1 addition & 0 deletions expression/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ var (
errDeprecatedSyntaxNoReplacement = terror.ClassExpression.New(mysql.ErrWarnDeprecatedSyntaxNoReplacement, mysql.MySQLErrName[mysql.ErrWarnDeprecatedSyntaxNoReplacement])
errBadField = terror.ClassExpression.New(mysql.ErrBadField, mysql.MySQLErrName[mysql.ErrBadField])
errWarnAllowedPacketOverflowed = terror.ClassExpression.New(mysql.ErrWarnAllowedPacketOverflowed, mysql.MySQLErrName[mysql.ErrWarnAllowedPacketOverflowed])
errWarnOptionIgnored = terror.ClassExpression.New(mysql.WarnOptionIgnored, mysql.MySQLErrName[mysql.WarnOptionIgnored])
)

func init() {
Expand Down
12 changes: 12 additions & 0 deletions expression/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -977,16 +977,28 @@ func (s *testIntegrationSuite) TestEncryptionBuiltin(c *C) {
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a char(10), b int, c double, d datetime, e time, f bit(4), g binary(20), h blob(10), i text(30))")
tk.MustExec(`insert into t values('2', 2, 2.3, "2017-01-01 12:01:01", "12:01:01", 0b1010, "512", "48", "tidb")`)
tk.MustExec("SET block_encryption_mode='aes-128-ecb';")
result = tk.MustQuery("select HEX(AES_ENCRYPT(a, 'key')), HEX(AES_ENCRYPT(b, 'key')), HEX(AES_ENCRYPT(c, 'key')), HEX(AES_ENCRYPT(d, 'key')), HEX(AES_ENCRYPT(e, 'key')), HEX(AES_ENCRYPT(f, 'key')), HEX(AES_ENCRYPT(g, 'key')), HEX(AES_ENCRYPT(h, 'key')), HEX(AES_ENCRYPT(i, 'key')) from t")
result.Check(testkit.Rows("B3800B3A3CB4ECE2051A3E80FE373EAC B3800B3A3CB4ECE2051A3E80FE373EAC 9E018F7F2838DBA23C57F0E4CCF93287 E764D3E9D4AF8F926CD0979DDB1D0AF40C208B20A6C39D5D028644885280973A C452FFEEB76D3F5E9B26B8D48F7A228C 181BD5C81CBD36779A3C9DD5FF486B35 CE15F14AC7FF4E56ECCF148DE60E4BEDBDB6900AD51383970A5F32C59B3AC6E3 E1B29995CCF423C75519790F54A08CD2 84525677E95AC97698D22E1125B67E92"))
result = tk.MustQuery("select HEX(AES_ENCRYPT('123', 'foobar')), HEX(AES_ENCRYPT(123, 'foobar')), HEX(AES_ENCRYPT('', 'foobar')), HEX(AES_ENCRYPT('你好', 'foobar')), AES_ENCRYPT(NULL, 'foobar')")
result.Check(testkit.Rows(`45ABDD5C4802EFA6771A94C43F805208 45ABDD5C4802EFA6771A94C43F805208 791F1AEB6A6B796E6352BF381895CA0E D0147E2EB856186F146D9F6DE33F9546 <nil>`))
tk.MustExec("SET block_encryption_mode='aes-128-cbc';")
result = tk.MustQuery("select HEX(AES_ENCRYPT(a, 'key', '1234567890123456')), HEX(AES_ENCRYPT(b, 'key', '1234567890123456')), HEX(AES_ENCRYPT(c, 'key', '1234567890123456')), HEX(AES_ENCRYPT(d, 'key', '1234567890123456')), HEX(AES_ENCRYPT(e, 'key', '1234567890123456')), HEX(AES_ENCRYPT(f, 'key', '1234567890123456')), HEX(AES_ENCRYPT(g, 'key', '1234567890123456')), HEX(AES_ENCRYPT(h, 'key', '1234567890123456')), HEX(AES_ENCRYPT(i, 'key', '1234567890123456')) from t")
result.Check(testkit.Rows("341672829F84CB6B0BE690FEC4C4DAE9 341672829F84CB6B0BE690FEC4C4DAE9 D43734E147A12BB96C6897C4BBABA283 16F2C972411948DCEF3659B726D2CCB04AD1379A1A367FA64242058A50211B67 41E71D0C58967C1F50EEC074523946D1 1117D292E2D39C3EAA3B435371BE56FC 8ACB7ECC0883B672D7BD1CFAA9FA5FAF5B731ADE978244CD581F114D591C2E7E D2B13C30937E3251AEDA73859BA32E4B 2CF4A6051FF248A67598A17AA2C17267"))
result = tk.MustQuery("select HEX(AES_ENCRYPT('123', 'foobar', '1234567890123456')), HEX(AES_ENCRYPT(123, 'foobar', '1234567890123456')), HEX(AES_ENCRYPT('', 'foobar', '1234567890123456')), HEX(AES_ENCRYPT('你好', 'foobar', '1234567890123456')), AES_ENCRYPT(NULL, 'foobar', '1234567890123456')")
result.Check(testkit.Rows(`80D5646F07B4654B05A02D9085759770 80D5646F07B4654B05A02D9085759770 B3C14BA15030D2D7E99376DBE011E752 0CD2936EE4FEC7A8CDF6208438B2BC05 <nil>`))

// for AES_DECRYPT
tk.MustExec("SET block_encryption_mode='aes-128-ecb';")
result = tk.MustQuery("select AES_DECRYPT(AES_ENCRYPT('foo', 'bar'), 'bar')")
result.Check(testkit.Rows("foo"))
result = tk.MustQuery("select AES_DECRYPT(UNHEX('45ABDD5C4802EFA6771A94C43F805208'), 'foobar'), AES_DECRYPT(UNHEX('791F1AEB6A6B796E6352BF381895CA0E'), 'foobar'), AES_DECRYPT(UNHEX('D0147E2EB856186F146D9F6DE33F9546'), 'foobar'), AES_DECRYPT(NULL, 'foobar'), AES_DECRYPT('SOME_THING_STRANGE', 'foobar')")
result.Check(testkit.Rows(`123 你好 <nil> <nil>`))
tk.MustExec("SET block_encryption_mode='aes-128-cbc';")
result = tk.MustQuery("select AES_DECRYPT(AES_ENCRYPT('foo', 'bar', '1234567890123456'), 'bar', '1234567890123456')")
result.Check(testkit.Rows("foo"))
result = tk.MustQuery("select AES_DECRYPT(UNHEX('80D5646F07B4654B05A02D9085759770'), 'foobar', '1234567890123456'), AES_DECRYPT(UNHEX('B3C14BA15030D2D7E99376DBE011E752'), 'foobar', '1234567890123456'), AES_DECRYPT(UNHEX('0CD2936EE4FEC7A8CDF6208438B2BC05'), 'foobar', '1234567890123456'), AES_DECRYPT(NULL, 'foobar', '1234567890123456'), AES_DECRYPT('SOME_THING_STRANGE', 'foobar', '1234567890123456')")
result.Check(testkit.Rows(`123 你好 <nil> <nil>`))

// for COMPRESS
tk.MustExec("DROP TABLE IF EXISTS t1;")
Expand Down
1 change: 1 addition & 0 deletions session/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -1250,6 +1250,7 @@ const loadCommonGlobalVarsSQL = "select HIGH_PRIORITY * from mysql.global_variab
variable.SQLModeVar + quoteCommaQuote +
variable.MaxAllowedPacket + quoteCommaQuote +
variable.TimeZone + quoteCommaQuote +
variable.BlockEncryptionMode + quoteCommaQuote +
/* TiDB specific global variables: */
variable.TiDBSkipUTF8Check + quoteCommaQuote +
variable.TiDBIndexJoinBatchSize + quoteCommaQuote +
Expand Down
4 changes: 3 additions & 1 deletion sessionctx/variable/sysvar.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ var defaultSysVars = []*SysVar{
{ScopeNone, "myisam_mmap_size", "18446744073709551615"},
{ScopeGlobal, "init_slave", ""},
{ScopeNone, "innodb_buffer_pool_instances", "8"},
{ScopeGlobal | ScopeSession, "block_encryption_mode", "aes-128-ecb"},
{ScopeGlobal | ScopeSession, BlockEncryptionMode, "aes-128-ecb"},
{ScopeGlobal | ScopeSession, "max_length_for_sort_data", "1024"},
{ScopeNone, "character_set_system", "utf8"},
{ScopeGlobal | ScopeSession, "interactive_timeout", "28800"},
Expand Down Expand Up @@ -745,6 +745,8 @@ const (
WarningCount = "warning_count"
// ErrorCount is the name for 'error_count' system variable.
ErrorCount = "error_count"
// BlockEncryptionMode is the name for 'block_encryption_mode' system variable.
BlockEncryptionMode = "block_encryption_mode"
)

// GlobalVarAccessor is the interface for accessing global scope system and status variables.
Expand Down
40 changes: 40 additions & 0 deletions util/encrypt/aes.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,43 @@ func DeriveKeyMySQL(key []byte, blockSize int) []byte {
}
return rKey
}

// AESEncryptWithCBC encrypts data using AES with CBC mode.
func AESEncryptWithCBC(str, key []byte, iv []byte) ([]byte, error) {
cb, err := aes.NewCipher(key)
if err != nil {
return nil, errors.Trace(err)
}
blockSize := cb.BlockSize()
// The str arguments can be any length, and padding is automatically added to
// str so it is a multiple of a block as required by block-based algorithms such as AES.
// This padding is automatically removed by the AES_DECRYPT() function.
data, err := PKCS7Pad(str, blockSize)
if err != nil {
return nil, err
}
cbc := cipher.NewCBCEncrypter(cb, iv)
crypted := make([]byte, len(data))
cbc.CryptBlocks(crypted, data)
return crypted, nil
}

// AESDecryptWithCBC decrypts data using AES with CBC mode.
func AESDecryptWithCBC(cryptStr, key []byte, iv []byte) ([]byte, error) {
cb, err := aes.NewCipher(key)
if err != nil {
return nil, errors.Trace(err)
}
blockSize := cb.BlockSize()
if len(cryptStr)%blockSize != 0 {
return nil, errors.New("Corrupted data")
}
cbc := cipher.NewCBCDecrypter(cb, iv)
data := make([]byte, len(cryptStr))
cbc.CryptBlocks(data, cryptStr)
plain, err := PKCS7Unpad(data, blockSize)
if err != nil {
return nil, err
}
return plain, nil
}
Loading