forked from microsoft/mssql-jdbc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathStreamColumns.java
226 lines (179 loc) · 7.9 KB
/
StreamColumns.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/*
* Microsoft JDBC Driver for SQL Server
*
* Copyright(c) Microsoft Corporation All rights reserved.
*
* This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information.
*/
package com.microsoft.sqlserver.jdbc;
/**
* StreamColumns stores the column meta data for a result set. StreamColumns parses the inbound TDS packet stream to determine column meta data.
*/
final class StreamColumns extends StreamPacket {
/** the set of columns */
private Column[] columns;
/* The CekTable. */
private CekTable cekTable = null;
private boolean shouldHonorAEForRead = false;
/* Gets the CekTable */
CekTable getCekTable() {
return cekTable;
}
/**
* Create a new stream columns
*/
/* L0 */ StreamColumns() {
super(TDS.TDS_COLMETADATA);
}
/**
* Create a new stream columns with column encryption setting
*/
StreamColumns(boolean honorAE) {
super(TDS.TDS_COLMETADATA);
shouldHonorAEForRead = honorAE;
}
/**
* Parse a result set column meta data TDS stream for CEK table entry.
*
* @throws SQLServerException
*/
CekTableEntry readCEKTableEntry(TDSReader tdsReader) throws SQLServerException {
// Read the DB ID
int databaseId = tdsReader.readInt();
// Read the keyID
int cekId = tdsReader.readInt();
// Read the key version
int cekVersion = tdsReader.readInt();
// Read the key MD Version
byte[] cekMdVersion = new byte[8];
tdsReader.readBytes(cekMdVersion, 0, 8);
// Read the value count
int cekValueCount = tdsReader.readUnsignedByte();
CekTableEntry cekTableEntry = new CekTableEntry(cekValueCount);
for (int i = 0; i < cekValueCount; i++) {
// Read individual CEK values
short encryptedCEKlength = tdsReader.readShort();
byte[] encryptedCek = new byte[encryptedCEKlength];
// Read the actual encrypted CEK
tdsReader.readBytes(encryptedCek, 0, encryptedCEKlength);
// Read the length of key store name
int keyStoreLength = tdsReader.readUnsignedByte();
// And read the key store name now
String keyStoreName = tdsReader.readUnicodeString(keyStoreLength);
// Read the length of key Path
int keyPathLength = tdsReader.readShort();
// Read the key path string
String keyPath = tdsReader.readUnicodeString(keyPathLength);
// Read the length of the string carrying the encryption algo
int algorithmLength = tdsReader.readUnsignedByte();
// Read the string carrying the encryption algo (eg. RSA_PKCS_OAEP)
String algorithmName = tdsReader.readUnicodeString(algorithmLength);
// Add this encrypted CEK blob to our list of encrypted values for the CEK
cekTableEntry.add(encryptedCek, databaseId, cekId, cekVersion, cekMdVersion, keyPath, keyStoreName, algorithmName);
}
return cekTableEntry;
}
/**
* Parse a result set column meta data TDS stream for CEK table.
*
* @throws SQLServerException
*/
void readCEKTable(TDSReader tdsReader) throws SQLServerException {
// Read count
int tableSize = tdsReader.readShort();
// CEK table will be sent if AE is enabled. If none of the columns are encrypted, the CEK table
// size would be zero.
if (0 != tableSize) {
cekTable = new CekTable(tableSize);
// Read individual entries
for (int i = 0; i < tableSize; i++) {
// SqlTceCipherInfoEntry entry;
cekTable.setCekTableEntry(i, readCEKTableEntry(tdsReader));
}
}
}
/**
* Parse a result set column meta data TDS stream for crypto metadata for AE.
*
* @throws SQLServerException
*/
CryptoMetadata readCryptoMetadata(TDSReader tdsReader) throws SQLServerException {
short ordinal = 0;
// Read the ordinal into cipher table. For return values there is not cipher table and no ordinal.
if (null != cekTable) {
ordinal = tdsReader.readShort();
}
TypeInfo typeInfo = TypeInfo.getInstance(tdsReader, false);
// Read the cipher algorithm Id
byte algorithmId = (byte) tdsReader.readUnsignedByte();
String algorithmName = null;
if ((byte) TDS.CUSTOM_CIPHER_ALGORITHM_ID == algorithmId) {
// Custom encryption algorithm, read the name
int nameSize = tdsReader.readUnsignedByte();
algorithmName = tdsReader.readUnicodeString(nameSize);
}
// Read Encryption Type.
byte encryptionType = (byte) tdsReader.readUnsignedByte();
// Read Normalization Rule Version.
byte normalizationRuleVersion = (byte) tdsReader.readUnsignedByte();
CryptoMetadata cryptoMeta = new CryptoMetadata((cekTable == null) ? null : cekTable.getCekTableEntry(ordinal), ordinal, algorithmId,
algorithmName, encryptionType, normalizationRuleVersion);
cryptoMeta.setBaseTypeInfo(typeInfo);
return cryptoMeta;
}
/**
* Parse a result set column meta data TDS stream.
*
* @throws SQLServerException
*/
void setFromTDS(TDSReader tdsReader) throws SQLServerException {
if (TDS.TDS_COLMETADATA != tdsReader.readUnsignedByte())
assert false;
int nTotColumns = tdsReader.readUnsignedShort();
// Handle the magic NoMetaData value
if (0xFFFF == nTotColumns)
return;
if (tdsReader.getServerSupportsColumnEncryption()) {
readCEKTable(tdsReader);
}
this.columns = new Column[nTotColumns];
for (int numColumns = 0; numColumns < nTotColumns; numColumns++) {
// Column type info
TypeInfo typeInfo = TypeInfo.getInstance(tdsReader, true);
// Table name
//
// Table name is set at this point for TEXT/NTEXT/IMAGE columns only.
// For other columns, table name may be set via COLUMNINFO and TABNAME tokens.
SQLIdentifier tableName = new SQLIdentifier();
if (SSType.TEXT == typeInfo.getSSType() || SSType.NTEXT == typeInfo.getSSType() || SSType.IMAGE == typeInfo.getSSType()) {
// Yukon and later, table names are returned as multi-part SQL identifiers.
tableName = tdsReader.readSQLIdentifier();
}
CryptoMetadata cryptoMeta = null;
if (tdsReader.getServerSupportsColumnEncryption() && typeInfo.isEncrypted()) {
cryptoMeta = readCryptoMetadata(tdsReader);
cryptoMeta.baseTypeInfo.setFlags(typeInfo.getFlagsAsShort());
typeInfo.setSQLCollation(cryptoMeta.baseTypeInfo.getSQLCollation());
}
// Column name
String columnName = tdsReader.readUnicodeString(tdsReader.readUnsignedByte());
if (shouldHonorAEForRead) {
this.columns[numColumns] = new Column(typeInfo, columnName, tableName, cryptoMeta);
}
else {
// Set null for crypto metadata if column encryption setting is off at the connection or at the statement level.
this.columns[numColumns] = new Column(typeInfo, columnName, tableName, null);
}
}
}
/**
* Applies per-column table information derived from COLINFO and TABNAME tokens to the set of columns defined by this COLMETADATA token to produce
* the complete set of column information.
*/
Column[] buildColumns(StreamColInfo colInfoToken,
StreamTabName tabNameToken) throws SQLServerException {
if (null != colInfoToken && null != tabNameToken)
tabNameToken.applyTo(columns, colInfoToken.applyTo(columns));
return columns;
}
}