Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
826 lines (748 sloc) 20 KB
// Copyright 2019 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,
// See the License for the specific language governing permissions and
// limitations under the License.
package expression
import (
"bytes"
"crypto/aes"
"crypto/md5"
"crypto/rand"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/binary"
"fmt"
"hash"
"io"
"sync"
"github.com/pingcap/errors"
"github.com/pingcap/parser/auth"
"github.com/pingcap/tidb/types"
"github.com/pingcap/tidb/util/chunk"
"github.com/pingcap/tidb/util/encrypt"
"github.com/pingcap/tidb/util/hack"
)
func (b *builtinAesDecryptSig) vectorized() bool {
return true
}
func (b *builtinAesDecryptSig) vecEvalString(input *chunk.Chunk, result *chunk.Column) error {
n := input.NumRows()
strBuf, err := b.bufAllocator.get(types.ETString, n)
if err != nil {
return err
}
defer b.bufAllocator.put(strBuf)
if err := b.args[0].VecEvalString(b.ctx, input, strBuf); err != nil {
return err
}
keyBuf, err := b.bufAllocator.get(types.ETString, n)
if err != nil {
return err
}
defer b.bufAllocator.put(keyBuf)
if err := b.args[1].VecEvalString(b.ctx, input, keyBuf); err != nil {
return err
}
if b.modeName != "ecb" {
return errors.Errorf("unsupported block encryption mode - %v", b.modeName)
}
isWarning := !b.ivRequired && len(b.args) == 3
isConstKey := b.args[1].ConstItem()
var key []byte
if isConstKey {
key = encrypt.DeriveKeyMySQL(keyBuf.GetBytes(0), b.keySize)
}
result.ReserveString(n)
stmtCtx := b.ctx.GetSessionVars().StmtCtx
for i := 0; i < n; i++ {
// According to doc: If either function argument is NULL, the function returns NULL.
if strBuf.IsNull(i) || keyBuf.IsNull(i) {
result.AppendNull()
continue
}
if isWarning {
// For modes that do not require init_vector, it is ignored and a warning is generated if it is specified.
stmtCtx.AppendWarning(errWarnOptionIgnored.GenWithStackByArgs("IV"))
}
if !isConstKey {
key = encrypt.DeriveKeyMySQL(keyBuf.GetBytes(i), b.keySize)
}
// ANNOTATION:
// we can't use GetBytes here because GetBytes return raw memory in strBuf,
// and the memory will be modified in AESEncryptWithECB & AESDecryptWithECB
str := []byte(strBuf.GetString(i))
plainText, err := encrypt.AESDecryptWithECB(str, key)
if err != nil {
result.AppendNull()
continue
}
result.AppendBytes(plainText)
}
return nil
}
func (b *builtinAesEncryptIVSig) vectorized() bool {
return true
}
// evalString evals AES_ENCRYPT(str, key_str, iv).
// See https://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html#function_aes-decrypt
func (b *builtinAesEncryptIVSig) vecEvalString(input *chunk.Chunk, result *chunk.Column) error {
n := input.NumRows()
strBuf, err := b.bufAllocator.get(types.ETString, n)
if err != nil {
return err
}
defer b.bufAllocator.put(strBuf)
if err := b.args[0].VecEvalString(b.ctx, input, strBuf); err != nil {
return err
}
keyBuf, err := b.bufAllocator.get(types.ETString, n)
if err != nil {
return err
}
defer b.bufAllocator.put(keyBuf)
if err := b.args[1].VecEvalString(b.ctx, input, keyBuf); err != nil {
return err
}
ivBuf, err := b.bufAllocator.get(types.ETString, n)
if err != nil {
return err
}
defer b.bufAllocator.put(ivBuf)
if err := b.args[2].VecEvalString(b.ctx, input, ivBuf); err != nil {
return err
}
isCBC := false
isOFB := false
isCFB := false
switch b.modeName {
case "cbc":
isCBC = true
case "ofb":
isOFB = true
case "cfb":
isCFB = true
default:
return errors.Errorf("unsupported block encryption mode - %v", b.modeName)
}
isConst := b.args[1].ConstItem()
var key []byte
if isConst {
key = encrypt.DeriveKeyMySQL(keyBuf.GetBytes(0), b.keySize)
}
result.ReserveString(n)
for i := 0; i < n; i++ {
// According to doc: If either function argument is NULL, the function returns NULL.
if strBuf.IsNull(i) || keyBuf.IsNull(i) || ivBuf.IsNull(i) {
result.AppendNull()
continue
}
iv := ivBuf.GetBytes(i)
if len(iv) < aes.BlockSize {
return errIncorrectArgs.GenWithStack("The initialization vector supplied to aes_encrypt is too short. Must be at least %d bytes long", aes.BlockSize)
}
// init_vector must be 16 bytes or longer (bytes in excess of 16 are ignored)
iv = iv[0:aes.BlockSize]
if !isConst {
key = encrypt.DeriveKeyMySQL(keyBuf.GetBytes(i), b.keySize)
}
var cipherText []byte
// ANNOTATION:
// we can't use GetBytes here because GetBytes return raw memory in strBuf,
// and the memory will be modified in AESEncryptWithCBC & AESEncryptWithOFB & AESEncryptWithCFB
if isCBC {
cipherText, err = encrypt.AESEncryptWithCBC([]byte(strBuf.GetString(i)), key, iv)
}
if isOFB {
cipherText, err = encrypt.AESEncryptWithOFB([]byte(strBuf.GetString(i)), key, iv)
}
if isCFB {
cipherText, err = encrypt.AESEncryptWithCFB([]byte(strBuf.GetString(i)), key, iv)
}
if err != nil {
result.AppendNull()
continue
}
result.AppendBytes(cipherText)
}
return nil
}
func (b *builtinDecodeSig) vectorized() bool {
return true
}
func (b *builtinDecodeSig) vecEvalString(input *chunk.Chunk, result *chunk.Column) error {
n := input.NumRows()
buf, err := b.bufAllocator.get(types.ETString, n)
if err != nil {
return err
}
defer b.bufAllocator.put(buf)
if err := b.args[0].VecEvalString(b.ctx, input, buf); err != nil {
return err
}
buf1, err1 := b.bufAllocator.get(types.ETString, n)
if err1 != nil {
return err1
}
defer b.bufAllocator.put(buf1)
if err := b.args[1].VecEvalString(b.ctx, input, buf1); err != nil {
return err
}
result.ReserveString(n)
for i := 0; i < n; i++ {
if buf.IsNull(i) || buf1.IsNull(i) {
result.AppendNull()
continue
}
dataStr := buf.GetString(i)
passwordStr := buf1.GetString(i)
decodeStr, err := encrypt.SQLDecode(dataStr, passwordStr)
if err != nil {
return err
}
result.AppendString(decodeStr)
}
return nil
}
func (b *builtinEncodeSig) vectorized() bool {
return true
}
func (b *builtinEncodeSig) vecEvalString(input *chunk.Chunk, result *chunk.Column) error {
n := input.NumRows()
buf, err := b.bufAllocator.get(types.ETString, n)
if err != nil {
return err
}
defer b.bufAllocator.put(buf)
if err := b.args[0].VecEvalString(b.ctx, input, buf); err != nil {
return err
}
buf1, err1 := b.bufAllocator.get(types.ETString, n)
if err1 != nil {
return err1
}
defer b.bufAllocator.put(buf1)
if err := b.args[1].VecEvalString(b.ctx, input, buf1); err != nil {
return err
}
result.ReserveString(n)
for i := 0; i < n; i++ {
if buf.IsNull(i) || buf1.IsNull(i) {
result.AppendNull()
continue
}
decodeStr := buf.GetString(i)
passwordStr := buf1.GetString(i)
dataStr, err := encrypt.SQLEncode(decodeStr, passwordStr)
if err != nil {
return err
}
result.AppendString(dataStr)
}
return nil
}
func (b *builtinAesDecryptIVSig) vectorized() bool {
return true
}
// evalString evals AES_DECRYPT(crypt_str, key_key, iv).
// See https://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html#function_aes-decrypt
func (b *builtinAesDecryptIVSig) vecEvalString(input *chunk.Chunk, result *chunk.Column) error {
n := input.NumRows()
strBuf, err := b.bufAllocator.get(types.ETString, n)
if err != nil {
return err
}
defer b.bufAllocator.put(strBuf)
if err := b.args[0].VecEvalString(b.ctx, input, strBuf); err != nil {
return err
}
keyBuf, err := b.bufAllocator.get(types.ETString, n)
if err != nil {
return err
}
defer b.bufAllocator.put(keyBuf)
if err := b.args[1].VecEvalString(b.ctx, input, keyBuf); err != nil {
return err
}
ivBuf, err := b.bufAllocator.get(types.ETString, n)
if err != nil {
return err
}
defer b.bufAllocator.put(ivBuf)
if err := b.args[2].VecEvalString(b.ctx, input, ivBuf); err != nil {
return err
}
isCBC := false
isOFB := false
isCFB := false
switch b.modeName {
case "cbc":
isCBC = true
case "ofb":
isOFB = true
case "cfb":
isCFB = true
default:
return errors.Errorf("unsupported block encryption mode - %v", b.modeName)
}
isConst := b.args[1].ConstItem()
var key []byte
if isConst {
key = encrypt.DeriveKeyMySQL(keyBuf.GetBytes(0), b.keySize)
}
result.ReserveString(n)
for i := 0; i < n; i++ {
// According to doc: If either function argument is NULL, the function returns NULL.
if strBuf.IsNull(i) || keyBuf.IsNull(i) || ivBuf.IsNull(i) {
result.AppendNull()
continue
}
iv := ivBuf.GetBytes(i)
if len(iv) < aes.BlockSize {
return errIncorrectArgs.GenWithStack("The initialization vector supplied to aes_decrypt is too short. Must be at least %d bytes long", aes.BlockSize)
}
// init_vector must be 16 bytes or longer (bytes in excess of 16 are ignored)
iv = iv[0:aes.BlockSize]
if !isConst {
key = encrypt.DeriveKeyMySQL(keyBuf.GetBytes(i), b.keySize)
}
var plainText []byte
// ANNOTATION:
// we can't use GetBytes here because GetBytes return raw memory in strBuf,
// and the memory will be modified in AESDecryptWithCBC & AESDecryptWithOFB & AESDecryptWithCFB
if isCBC {
plainText, err = encrypt.AESDecryptWithCBC([]byte(strBuf.GetString(i)), key, iv)
}
if isOFB {
plainText, err = encrypt.AESDecryptWithOFB([]byte(strBuf.GetString(i)), key, iv)
}
if isCFB {
plainText, err = encrypt.AESDecryptWithCFB([]byte(strBuf.GetString(i)), key, iv)
}
if err != nil {
result.AppendNull()
continue
}
result.AppendBytes(plainText)
}
return nil
}
func (b *builtinRandomBytesSig) vectorized() bool {
return true
}
func (b *builtinRandomBytesSig) vecEvalString(input *chunk.Chunk, result *chunk.Column) error {
n := input.NumRows()
buf, err := b.bufAllocator.get(types.ETInt, n)
if err != nil {
return err
}
defer b.bufAllocator.put(buf)
if err := b.args[0].VecEvalInt(b.ctx, input, buf); err != nil {
return err
}
result.ReserveString(n)
i64s := buf.Int64s()
var dst bytes.Buffer
for i := 0; i < n; i++ {
if buf.IsNull(i) {
result.AppendNull()
continue
}
byteLen := i64s[i]
if byteLen < 1 || byteLen > 1024 {
return types.ErrOverflow.GenWithStackByArgs("length", "random_bytes")
}
if n, err := io.CopyN(&dst, rand.Reader, byteLen); err != nil {
return err
} else if n != byteLen {
return errors.New("fail to generate random bytes")
}
result.AppendBytes(dst.Bytes())
dst.Reset()
}
return nil
}
func (b *builtinMD5Sig) vectorized() bool {
return true
}
func (b *builtinMD5Sig) vecEvalString(input *chunk.Chunk, result *chunk.Column) error {
n := input.NumRows()
buf, err := b.bufAllocator.get(types.ETString, n)
if err != nil {
return err
}
defer b.bufAllocator.put(buf)
if err := b.args[0].VecEvalString(b.ctx, input, buf); err != nil {
return err
}
result.ReserveString(n)
digest := md5.New()
for i := 0; i < n; i++ {
if buf.IsNull(i) {
result.AppendNull()
continue
}
cryptByte := buf.GetBytes(i)
_, err := digest.Write(cryptByte)
if err != nil {
return err
}
result.AppendString(fmt.Sprintf("%x", digest.Sum(nil)))
digest.Reset()
}
return nil
}
func (b *builtinSHA2Sig) vectorized() bool {
return true
}
// vecEvalString evals SHA2(str, hash_length).
// See https://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html#function_sha2
func (b *builtinSHA2Sig) vecEvalString(input *chunk.Chunk, result *chunk.Column) error {
n := input.NumRows()
buf, err := b.bufAllocator.get(types.ETString, n)
if err != nil {
return err
}
defer b.bufAllocator.put(buf)
if err := b.args[0].VecEvalString(b.ctx, input, buf); err != nil {
return err
}
buf1, err := b.bufAllocator.get(types.ETInt, n)
if err != nil {
return err
}
defer b.bufAllocator.put(buf1)
if err := b.args[1].VecEvalInt(b.ctx, input, buf1); err != nil {
return err
}
result.ReserveString(n)
i64s := buf1.Int64s()
var (
hasher224 hash.Hash
hasher256 hash.Hash
hasher384 hash.Hash
hasher512 hash.Hash
)
for i := 0; i < n; i++ {
if buf.IsNull(i) || buf1.IsNull(i) {
result.AppendNull()
continue
}
hashLength := i64s[i]
var hasher hash.Hash
switch int(hashLength) {
case SHA0, SHA256:
if hasher256 == nil {
hasher256 = sha256.New()
}
hasher = hasher256
case SHA224:
if hasher224 == nil {
hasher224 = sha256.New224()
}
hasher = hasher224
case SHA384:
if hasher384 == nil {
hasher384 = sha512.New384()
}
hasher = hasher384
case SHA512:
if hasher512 == nil {
hasher512 = sha512.New()
}
hasher = hasher512
}
if hasher == nil {
result.AppendNull()
continue
}
str := buf.GetBytes(i)
_, err = hasher.Write(str)
if err != nil {
return err
}
result.AppendString(fmt.Sprintf("%x", hasher.Sum(nil)))
hasher.Reset()
}
return nil
}
func (b *builtinCompressSig) vectorized() bool {
return true
}
var (
defaultByteSliceSize = 1024
bytePool = sync.Pool{
New: func() interface{} {
return make([]byte, defaultByteSliceSize)
},
}
)
func allocByteSlice(n int) []byte {
if n > defaultByteSliceSize {
return make([]byte, n)
}
return bytePool.Get().([]byte)
}
func deallocateByteSlice(b []byte) {
if cap(b) <= defaultByteSliceSize {
bytePool.Put(b)
}
}
// evalString evals COMPRESS(str).
// See https://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html#function_compress
func (b *builtinCompressSig) vecEvalString(input *chunk.Chunk, result *chunk.Column) error {
n := input.NumRows()
buf, err := b.bufAllocator.get(types.ETString, n)
if err != nil {
return err
}
defer b.bufAllocator.put(buf)
if err := b.args[0].VecEvalString(b.ctx, input, buf); err != nil {
return err
}
result.ReserveString(n)
for i := 0; i < n; i++ {
if buf.IsNull(i) {
result.AppendNull()
continue
}
str := buf.GetString(i)
// According to doc: Empty strings are stored as empty strings.
if len(str) == 0 {
result.AppendString("")
}
compressed, err := deflate(hack.Slice(str))
if err != nil {
result.AppendNull()
continue
}
resultLength := 4 + len(compressed)
// append "." if ends with space
shouldAppendSuffix := compressed[len(compressed)-1] == 32
if shouldAppendSuffix {
resultLength++
}
buffer := allocByteSlice(resultLength)
defer deallocateByteSlice(buffer)
buffer = buffer[:resultLength]
binary.LittleEndian.PutUint32(buffer, uint32(len(str)))
copy(buffer[4:], compressed)
if shouldAppendSuffix {
buffer[len(buffer)-1] = '.'
}
result.AppendBytes(buffer)
}
return nil
}
func (b *builtinAesEncryptSig) vectorized() bool {
return true
}
// evalString evals AES_ENCRYPT(str, key_str).
// See https://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html#function_aes-decrypt
func (b *builtinAesEncryptSig) vecEvalString(input *chunk.Chunk, result *chunk.Column) error {
n := input.NumRows()
strBuf, err := b.bufAllocator.get(types.ETString, n)
if err != nil {
return err
}
defer b.bufAllocator.put(strBuf)
if err := b.args[0].VecEvalString(b.ctx, input, strBuf); err != nil {
return err
}
keyBuf, err := b.bufAllocator.get(types.ETString, n)
if err != nil {
return err
}
defer b.bufAllocator.put(keyBuf)
if err := b.args[1].VecEvalString(b.ctx, input, keyBuf); err != nil {
return err
}
if b.modeName != "ecb" {
// For modes that do not require init_vector, it is ignored and a warning is generated if it is specified.
return errors.Errorf("unsupported block encryption mode - %v", b.modeName)
}
isWarning := !b.ivRequired && len(b.args) == 3
isConst := b.args[1].ConstItem()
var key []byte
if isConst {
key = encrypt.DeriveKeyMySQL(keyBuf.GetBytes(0), b.keySize)
}
sc := b.ctx.GetSessionVars().StmtCtx
result.ReserveString(n)
for i := 0; i < n; i++ {
// According to doc: If either function argument is NULL, the function returns NULL.
if strBuf.IsNull(i) || keyBuf.IsNull(i) {
result.AppendNull()
continue
}
if isWarning {
sc.AppendWarning(errWarnOptionIgnored.GenWithStackByArgs("IV"))
}
if !isConst {
key = encrypt.DeriveKeyMySQL(keyBuf.GetBytes(i), b.keySize)
}
// NOTE: we can't use GetBytes, because in AESEncryptWithECB padding is automatically
// added to str and this will damange the data layout in chunk.Column
str := []byte(strBuf.GetString(i))
cipherText, err := encrypt.AESEncryptWithECB(str, key)
if err != nil {
result.AppendNull()
continue
}
result.AppendBytes(cipherText)
}
return nil
}
func (b *builtinPasswordSig) vectorized() bool {
return true
}
func (b *builtinPasswordSig) vecEvalString(input *chunk.Chunk, result *chunk.Column) error {
n := input.NumRows()
buf, err := b.bufAllocator.get(types.ETString, n)
if err != nil {
return err
}
defer b.bufAllocator.put(buf)
if err := b.args[0].VecEvalString(b.ctx, input, buf); err != nil {
return err
}
result.ReserveString(n)
for i := 0; i < n; i++ {
if buf.IsNull(i) {
result.AppendString("")
continue
}
pass := buf.GetString(i)
if len(pass) == 0 {
result.AppendString("")
continue
}
// We should append a warning here because function "PASSWORD" is deprecated since MySQL 5.7.6.
// See https://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html#function_password
b.ctx.GetSessionVars().StmtCtx.AppendWarning(errDeprecatedSyntaxNoReplacement.GenWithStackByArgs("PASSWORD"))
result.AppendString(auth.EncodePassword(pass))
}
return nil
}
func (b *builtinSHA1Sig) vectorized() bool {
return true
}
func (b *builtinSHA1Sig) vecEvalString(input *chunk.Chunk, result *chunk.Column) error {
n := input.NumRows()
buf, err := b.bufAllocator.get(types.ETString, n)
if err != nil {
return err
}
defer b.bufAllocator.put(buf)
if err := b.args[0].VecEvalString(b.ctx, input, buf); err != nil {
return err
}
result.ReserveString(n)
hasher := sha1.New()
for i := 0; i < n; i++ {
if buf.IsNull(i) {
result.AppendNull()
continue
}
str := buf.GetBytes(i)
_, err = hasher.Write(str)
if err != nil {
return err
}
result.AppendString(fmt.Sprintf("%x", hasher.Sum(nil)))
hasher.Reset()
}
return nil
}
func (b *builtinUncompressSig) vectorized() bool {
return true
}
// evalString evals UNCOMPRESS(compressed_string).
// See https://dev.mysql.com/doc/refman/5.7/en/encryption-functions.html#function_uncompress
func (b *builtinUncompressSig) vecEvalString(input *chunk.Chunk, result *chunk.Column) error {
n := input.NumRows()
buf, err := b.bufAllocator.get(types.ETString, n)
if err != nil {
return err
}
defer b.bufAllocator.put(buf)
if err := b.args[0].VecEvalString(b.ctx, input, buf); err != nil {
return err
}
result.ReserveString(n)
sc := b.ctx.GetSessionVars().StmtCtx
for i := 0; i < n; i++ {
if buf.IsNull(i) {
result.AppendNull()
continue
}
payload := buf.GetString(i)
if len(payload) == 0 {
result.AppendString("")
continue
}
if len(payload) <= 4 {
// corrupted
sc.AppendWarning(errZlibZData)
result.AppendNull()
continue
}
length := binary.LittleEndian.Uint32([]byte(payload[0:4]))
bytes, err := inflate([]byte(payload[4:]))
if err != nil {
sc.AppendWarning(errZlibZData)
result.AppendNull()
continue
}
if length < uint32(len(bytes)) {
sc.AppendWarning(errZlibZBuf)
result.AppendNull()
continue
}
result.AppendBytes(bytes)
}
return nil
}
func (b *builtinUncompressedLengthSig) vectorized() bool {
return true
}
func (b *builtinUncompressedLengthSig) vecEvalInt(input *chunk.Chunk, result *chunk.Column) error {
sc := b.ctx.GetSessionVars().StmtCtx
nr := input.NumRows()
payloadBuf, err := b.bufAllocator.get(types.ETString, nr)
if err != nil {
return err
}
defer b.bufAllocator.put(payloadBuf)
if err := b.args[0].VecEvalString(b.ctx, input, payloadBuf); err != nil {
return err
}
result.ResizeInt64(nr, false)
result.MergeNulls(payloadBuf)
i64s := result.Int64s()
for i := 0; i < nr; i++ {
if result.IsNull(i) {
continue
}
str := payloadBuf.GetString(i)
if len(str) == 0 {
i64s[i] = 0
continue
}
if len(str) <= 4 {
sc.AppendWarning(errZlibZData)
i64s[i] = 0
continue
}
i64s[i] = int64(binary.LittleEndian.Uint32([]byte(str)[0:4]))
}
return nil
}
You can’t perform that action at this time.