Skip to content

Commit dbb883f

Browse files
committed
AE unicode data corruption guard
1 parent 84f8012 commit dbb883f

File tree

8 files changed

+323
-19
lines changed

8 files changed

+323
-19
lines changed

src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -415,19 +415,21 @@ void setValue(JDBCType jdbcType, Object value, JavaType javaType, StreamSetterAr
415415
// the value with the appropriate corresponding Unicode type.
416416
// JavaType.OBJECT == javaType when calling setNull()
417417
if (con.sendStringParametersAsUnicode() && (JavaType.STRING == javaType || JavaType.READER == javaType
418-
|| JavaType.CLOB == javaType || JavaType.OBJECT == javaType) && jdbcType != JDBCType.VARCHAR) {
418+
|| JavaType.CLOB == javaType || JavaType.OBJECT == javaType) && !((jdbcType == JDBCType.VARCHAR || jdbcType == JDBCType.CHAR) && con.isColumnEncryptionSettingEnabled())) {
419419
jdbcType = getSSPAUJDBCType(jdbcType);
420420
}
421421

422422
DTV newDTV = new DTV();
423423
newDTV.setValue(con.getDatabaseCollation(), jdbcType, value, javaType, streamSetterArgs, calendar, scale, con,
424424
forceEncrypt);
425425

426-
if (!con.sendStringParametersAsUnicode() || (con.sendStringParametersAsUnicode() && jdbcType == JDBCType.VARCHAR)) {
426+
if (!con.sendStringParametersAsUnicode() || (con.sendStringParametersAsUnicode()
427+
&& con.isColumnEncryptionSettingEnabled() && (jdbcType == JDBCType.VARCHAR || jdbcType == JDBCType.CHAR))) {
427428
newDTV.sendStringParametersAsUnicode = false;
428429
}
429430

430-
if (con.sendStringParametersAsUnicode() && jdbcType == JDBCType.VARCHAR && (!con.getDatabaseCollation().isUtf8Encoding() || con.getServerMajorVersion() < 15)) {
431+
if (con.sendStringParametersAsUnicode() && (jdbcType == JDBCType.VARCHAR || jdbcType == JDBCType.CHAR) && con.isColumnEncryptionSettingEnabled()
432+
&& (!con.getDatabaseCollation().isUtf8Encoding() || con.getServerMajorVersion() < 15)) {
431433
throw new SQLServerException(SQLServerException.getErrString("R_possibleColumnDataCorruption"), null);
432434
}
433435

@@ -812,7 +814,7 @@ private void setTypeDefinition(DTV dtv) {
812814
} else {
813815
param.typeDefinition = SSType.VARCHAR.toString() + "(" + valueLength + ")";
814816

815-
if (DataTypes.SHORT_VARTYPE_MAX_BYTES <= valueLength) {
817+
if (DataTypes.SHORT_VARTYPE_MAX_BYTES < valueLength) {
816818
param.typeDefinition = VARCHAR_MAX;
817819
}
818820
}

src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/AESetup.java

Lines changed: 147 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.io.FileReader;
1212
import java.io.IOException;
1313
import java.math.BigDecimal;
14+
import java.sql.Connection;
1415
import java.sql.Date;
1516
import java.sql.JDBCType;
1617
import java.sql.SQLException;
@@ -62,7 +63,6 @@ public class AESetup extends AbstractTest {
6263
static String cekWin = Constants.CEK_NAME + "_WIN";
6364
static String cekAkv = Constants.CEK_NAME + "_AKV";
6465
static SQLServerStatementColumnEncryptionSetting stmtColEncSetting = null;
65-
6666
static String AETestConnectionString;
6767
static String enclaveProperties = "";
6868

@@ -73,6 +73,8 @@ public class AESetup extends AbstractTest {
7373
.escapeSingleQuotes(AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("AETest_")));
7474
public static final String CHAR_TABLE_AE = TestUtils
7575
.escapeSingleQuotes(AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("JDBCEncryptedChar")));
76+
public static final String CHAR_TABLE_AE_NON_UNICODE = TestUtils
77+
.escapeSingleQuotes(AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("JDBCEncryptedCharNonUnicode")));
7678
public static final String BINARY_TABLE_AE = TestUtils
7779
.escapeSingleQuotes(AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("JDBCEncryptedBinary")));
7880
public static final String DATE_TABLE_AE = TestUtils
@@ -107,6 +109,16 @@ enum ColumnType {
107109
{"Varchar8000", "varchar(8000) COLLATE Latin1_General_BIN2", "CHAR"},
108110
{"Nvarchar4000", "nvarchar(4000) COLLATE Latin1_General_BIN2", "NCHAR"},};
109111

112+
static String charTableNonUnicode[][] = {{"Char", "char(20) COLLATE Latin1_General_BIN2", "CHAR"},
113+
{"Varchar", "varchar(50) COLLATE Latin1_General_BIN2", "CHAR"},
114+
{"VarcharMax", "varchar(max) COLLATE Latin1_General_BIN2", "LONGVARCHAR"},
115+
{"Varchar8000", "varchar(8000) COLLATE Latin1_General_BIN2", "CHAR"},};
116+
117+
static String charTableUTF8Collate[][] = {{"Char", "char(20) COLLATE Latin1_General_100_BIN2_UTF8", "CHAR"},
118+
{"Varchar", "varchar(50) COLLATE Latin1_General_100_BIN2_UTF8", "CHAR"},
119+
{"VarcharMax", "varchar(max) COLLATE Latin1_General_100_BIN2_UTF8", "LONGVARCHAR"},
120+
{"Varchar8000", "varchar(8000) COLLATE Latin1_General_100_BIN2_UTF8", "CHAR"},};
121+
110122
static String dateTable[][] = {{"Date", "date", "DATE"}, {"Datetime2Default", "datetime2", "TIMESTAMP"},
111123
{"DatetimeoffsetDefault", "datetimeoffset", "DATETIMEOFFSET"}, {"TimeDefault", "time", "TIME"},
112124
{"Datetime", "datetime", "DATETIME"}, {"Smalldatetime", "smalldatetime", "SMALLDATETIME"}};
@@ -338,6 +350,25 @@ protected static void createTable(String tableName, String cekName, String table
338350
}
339351
}
340352

353+
protected static void createTable(String tableName, String cekName, String table[][], SQLServerStatement stmt) {
354+
try {
355+
String sql = "";
356+
for (int i = 0; i < table.length; i++) {
357+
sql += ColumnType.PLAIN.name() + table[i][0] + " " + table[i][1] + " NULL,";
358+
sql += ColumnType.DETERMINISTIC.name() + table[i][0] + " " + table[i][1]
359+
+ String.format(encryptSql, ColumnType.DETERMINISTIC.name(), cekName) + ") NULL,";
360+
sql += ColumnType.RANDOMIZED.name() + table[i][0] + " " + table[i][1]
361+
+ String.format(encryptSql, ColumnType.RANDOMIZED.name(), cekName) + ") NULL,";
362+
}
363+
TestUtils.dropTableIfExists(tableName, stmt);
364+
sql = String.format(createSql, tableName, sql);
365+
stmt.execute(sql);
366+
stmt.execute("DBCC FREEPROCCACHE");
367+
} catch (SQLException e) {
368+
fail(e.getMessage());
369+
}
370+
}
371+
341372
protected static void createPrecisionTable(String tableName, String table[][], String cekName, int floatPrecision,
342373
int precision, int scale) throws SQLException {
343374
try (SQLServerConnection con = (SQLServerConnection) PrepUtil.getConnection(AETestConnectionString, AEInfo);
@@ -400,6 +431,22 @@ protected static void createScaleTable(String tableName, String table[][], Strin
400431
}
401432
}
402433

434+
protected static void createDatabaseWithUtf8Collation(Connection conn, String dbName) throws SQLException {
435+
try (SQLServerStatement stmt = (SQLServerStatement) conn.createStatement()) {
436+
String dropDB = "IF EXISTS (SELECT name FROM sys.databases WHERE name = N'"+ dbName + "') DROP DATABASE " + dbName + ";";
437+
String createDB = "CREATE DATABASE " + dbName + " COLLATE Latin1_General_100_CS_AS_WS_SC_UTF8";
438+
stmt.execute(dropDB);
439+
stmt.execute(createDB);
440+
}
441+
}
442+
443+
protected static void dropDatabaseWithUtf8Collation(Connection conn, String dbName) throws SQLException {
444+
try (SQLServerStatement stmt = (SQLServerStatement) conn.createStatement()) {
445+
String dropDB = "IF EXISTS (SELECT name FROM sys.databases WHERE name = N'"+ dbName + "') DROP DATABASE " + dbName + ";";
446+
stmt.execute(dropDB);
447+
}
448+
}
449+
403450
/**
404451
* Create a list of binary values
405452
*
@@ -449,6 +496,24 @@ protected static String[] createCharValues(boolean nullable) {
449496
return values;
450497
}
451498

499+
/**
500+
* Create a list of char values for non-unicode data types
501+
*
502+
* @param nullable
503+
*/
504+
protected static String[] createCharValuesNonUnicode(boolean nullable) {
505+
506+
boolean encrypted = true;
507+
String char20 = RandomData.generateCharTypes("20", nullable, encrypted);
508+
String varchar50 = RandomData.generateCharTypes("50", nullable, encrypted);
509+
String varcharmax = RandomData.generateCharTypes("max", nullable, encrypted);
510+
String varchar8000 = RandomData.generateCharTypes("8000", nullable, encrypted);
511+
512+
String[] values = {char20.trim(), varchar50, varcharmax, varchar8000};
513+
514+
return values;
515+
}
516+
452517
/**
453518
* Create a list of numeric values
454519
*
@@ -805,11 +870,12 @@ protected static void populateBinaryNullCase() throws SQLException {
805870
* @param charValues
806871
* @throws SQLException
807872
*/
808-
protected static void populateCharNormalCase(String[] charValues) throws SQLException {
873+
protected static void populateCharNormalCase(String[] charValues, boolean sendStringParametersAsUnicode) throws SQLException {
809874
String sql = "insert into " + CHAR_TABLE_AE + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?,"
810875
+ "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?" + ")";
811876

812-
try (SQLServerConnection con = (SQLServerConnection) PrepUtil.getConnection(AETestConnectionString, AEInfo);
877+
String connectionString = TestUtils.addOrOverrideProperty(AETestConnectionString, "sendStringParametersAsUnicode", Boolean.toString(sendStringParametersAsUnicode));
878+
try (SQLServerConnection con = (SQLServerConnection) PrepUtil.getConnection(connectionString, AEInfo);
813879
SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) TestUtils.getPreparedStmt(con, sql,
814880
stmtColEncSetting)) {
815881

@@ -866,6 +932,82 @@ protected static void populateCharNormalCase(String[] charValues) throws SQLExce
866932
}
867933
}
868934

935+
/**
936+
* Populate char data non-unicode.
937+
*
938+
* @param charValues
939+
* @throws SQLException
940+
*/
941+
protected static void populateCharNormalCaseNonUnicode(String connectionString, String[] charValues, boolean sendStringParametersAsUnicode) throws SQLException {
942+
String sql = "insert into " + CHAR_TABLE_AE_NON_UNICODE + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?)";
943+
944+
String cs = TestUtils.addOrOverrideProperty(connectionString, "sendStringParametersAsUnicode", Boolean.toString(sendStringParametersAsUnicode));
945+
try (SQLServerConnection con = (SQLServerConnection) PrepUtil.getConnection(cs, AEInfo);
946+
SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) TestUtils.getPreparedStmt(con, sql,
947+
stmtColEncSetting)) {
948+
949+
// char
950+
for (int i = 1; i <= 3; i++) {
951+
pstmt.setString(i, charValues[0]);
952+
}
953+
954+
// varchar
955+
for (int i = 4; i <= 6; i++) {
956+
pstmt.setString(i, charValues[1]);
957+
}
958+
959+
// varchar(max)
960+
for (int i = 7; i <= 9; i++) {
961+
pstmt.setString(i, charValues[2]);
962+
}
963+
964+
// varchar8000
965+
for (int i = 10; i <= 12; i++) {
966+
pstmt.setString(i, charValues[3]);
967+
}
968+
969+
pstmt.execute();
970+
}
971+
}
972+
973+
/**
974+
* Populate char data using set object.
975+
*
976+
* @param charValues
977+
* @throws SQLException
978+
*/
979+
protected static void populateCharSetObjectNonUnicode(String connectionString, String[] charValues, boolean sendStringParametersAsUnicode) throws SQLException {
980+
String sql = "insert into " + CHAR_TABLE_AE_NON_UNICODE + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?)";
981+
982+
String cs = TestUtils.addOrOverrideProperty(connectionString, "sendStringParametersAsUnicode", Boolean.toString(sendStringParametersAsUnicode));
983+
try (SQLServerConnection con = (SQLServerConnection) PrepUtil.getConnection(cs, AEInfo);
984+
SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) TestUtils.getPreparedStmt(con, sql,
985+
stmtColEncSetting)) {
986+
987+
// char
988+
for (int i = 1; i <= 3; i++) {
989+
pstmt.setObject(i, charValues[0]);
990+
}
991+
992+
// varchar
993+
for (int i = 4; i <= 6; i++) {
994+
pstmt.setObject(i, charValues[1]);
995+
}
996+
997+
// varchar(max)
998+
for (int i = 7; i <= 9; i++) {
999+
pstmt.setObject(i, charValues[2], java.sql.Types.LONGVARCHAR);
1000+
}
1001+
1002+
// varchar8000
1003+
for (int i = 10; i <= 12; i++) {
1004+
pstmt.setObject(i, charValues[3]);
1005+
}
1006+
1007+
pstmt.execute();
1008+
}
1009+
}
1010+
8691011
/**
8701012
* Populate char data using set object.
8711013
*
@@ -876,7 +1018,8 @@ protected static void populateCharSetObject(String[] charValues) throws SQLExcep
8761018
String sql = "insert into " + CHAR_TABLE_AE + " values( " + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?,"
8771019
+ "?,?,?," + "?,?,?," + "?,?,?," + "?,?,?" + ")";
8781020

879-
try (SQLServerConnection con = (SQLServerConnection) PrepUtil.getConnection(AETestConnectionString, AEInfo);
1021+
String connectionString = TestUtils.addOrOverrideProperty(AETestConnectionString, "sendStringParametersAsUnicode", "false");
1022+
try (SQLServerConnection con = (SQLServerConnection) PrepUtil.getConnection(connectionString, AEInfo);
8801023
SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) TestUtils.getPreparedStmt(con, sql,
8811024
stmtColEncSetting)) {
8821025

src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/CallableStatementTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ public static void initValues() throws Exception {
149149
createTable(BINARY_TABLE_AE, cekJks, binaryTable);
150150

151151
createDateTableCallableStatement(cekJks);
152-
populateCharNormalCase(charValues);
152+
populateCharNormalCase(charValues, false);
153153
populateNumericSetObject(numericValues);
154154
populateBinaryNormalCase(byteValues);
155155
populateDateNormalCase();

src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/EnclaveTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ public void testAEv2Disabled(String serverName, String url, String protocol) thr
187187
String[] values = createCharValues(false);
188188
TestUtils.dropTableIfExists(CHAR_TABLE_AE, stmt);
189189
createTable(CHAR_TABLE_AE, cekJks, charTable);
190-
populateCharNormalCase(values);
190+
populateCharNormalCase(values, false);
191191
testAlterColumnEncryption(stmt, CHAR_TABLE_AE, charTable, cekJks);
192192
fail(TestResource.getResource("R_expectedExceptionNotThrown"));
193193
} catch (Throwable e) {
@@ -346,7 +346,7 @@ public void testChar(String serverName, String url, String protocol) throws Exce
346346
SQLServerStatement stmt = (SQLServerStatement) con.createStatement()) {
347347
TestUtils.dropTableIfExists(CHAR_TABLE_AE, stmt);
348348
createTable(CHAR_TABLE_AE, cekJks, charTable);
349-
populateCharNormalCase(createCharValues(false));
349+
populateCharNormalCase(createCharValues(false), false);
350350
testAlterColumnEncryption(stmt, CHAR_TABLE_AE, charTable, cekJks);
351351
}
352352
}
@@ -363,7 +363,7 @@ public void testCharAkv(String serverName, String url, String protocol) throws E
363363
SQLServerStatement stmt = (SQLServerStatement) con.createStatement()) {
364364
TestUtils.dropTableIfExists(CHAR_TABLE_AE, stmt);
365365
createTable(CHAR_TABLE_AE, cekAkv, charTable);
366-
populateCharNormalCase(createCharValues(false));
366+
populateCharNormalCase(createCharValues(false), false);
367367
testAlterColumnEncryption(stmt, CHAR_TABLE_AE, charTable, cekAkv);
368368
}
369369
}

0 commit comments

Comments
 (0)