Always Encrypted v2 support (#1045)
* Change to support ae-v2 * Add support for AE V2 * Added some descriptions and comments * Fixed PDO pattern matching * Updated key generation scripts * Fixed key script * Fixed char/nchar results, fixed formatting issues * Addressed review comments * Updated key scripts * Debugging aev2 keyword failure * Debugging aev2 keyword failure * Debugging aev2 keyword failure * Debugging aev2 keyword failure * Added skipif to ae v2 keyword test * Addressed review comments * Fixed braces and camel caps * Updated test descriptions * Added detail to test descriptions * Tiny change
This commit is contained in:
parent
aec733b764
commit
051328782d
|
@ -718,14 +718,14 @@ bool core_is_conn_opt_value_escaped( _Inout_ const char* value, _Inout_ size_t v
|
|||
|
||||
const char *pch = strchr(pstr, '}');
|
||||
size_t i = 0;
|
||||
|
||||
|
||||
while (pch != NULL && i < value_len) {
|
||||
i = pch - pstr + 1;
|
||||
|
||||
|
||||
if (i == value_len || (i < value_len && pstr[i] != '}')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
i++; // skip the brace
|
||||
pch = strchr(pch + 2, '}'); // continue searching
|
||||
}
|
||||
|
@ -783,7 +783,7 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
|
|||
|
||||
try {
|
||||
// Since connection options access token and authentication cannot coexist, check if both of them are used.
|
||||
// If access token is specified, check UID and PWD as well.
|
||||
// If access token is specified, check UID and PWD as well.
|
||||
// No need to check the keyword Trusted_Connection because it is not among the acceptable options for SQLSRV drivers
|
||||
if (zend_hash_index_exists(options, SQLSRV_CONN_OPTION_ACCESS_TOKEN)) {
|
||||
bool invalidOptions = false;
|
||||
|
@ -801,7 +801,7 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
|
|||
access_token_used = true;
|
||||
}
|
||||
|
||||
// Check if Authentication is ActiveDirectoryMSI
|
||||
// Check if Authentication is ActiveDirectoryMSI
|
||||
// https://docs.microsoft.com/en-ca/azure/active-directory/managed-identities-azure-resources/overview
|
||||
bool activeDirectoryMSI = false;
|
||||
if (authentication_option_used) {
|
||||
|
@ -813,7 +813,7 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
|
|||
if (!stricmp(option, AzureADOptions::AZURE_AUTH_AD_MSI)) {
|
||||
activeDirectoryMSI = true;
|
||||
|
||||
// There are two types of managed identities:
|
||||
// There are two types of managed identities:
|
||||
// (1) A system-assigned managed identity: UID must be NULL
|
||||
// (2) A user-assigned managed identity: UID defined but must not be an empty string
|
||||
// In both cases, PWD must be NULL
|
||||
|
@ -832,11 +832,11 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add the server name
|
||||
common_conn_str_append_func( ODBCConnOptions::SERVER, server, strnlen_s( server ), connection_string TSRMLS_CC );
|
||||
|
||||
// If uid is not present then we use trusted connection -- but not when access token or ActiveDirectoryMSI is used,
|
||||
// If uid is not present then we use trusted connection -- but not when access token or ActiveDirectoryMSI is used,
|
||||
// because they are incompatible
|
||||
if (!access_token_used && !activeDirectoryMSI) {
|
||||
if (uid == NULL || strnlen_s(uid) == 0) {
|
||||
|
@ -1153,9 +1153,12 @@ void column_encryption_set_func::func( _In_ connection_option const* option, _In
|
|||
convert_to_string( value );
|
||||
const char* value_str = Z_STRVAL_P( value );
|
||||
|
||||
// Column Encryption is disabled by default unless it is explicitly 'Enabled'
|
||||
// Column Encryption is disabled by default, but if it is present and not
|
||||
// explicitly set to disabled or enabled, the ODBC driver will assume the
|
||||
// user is providing an attestation protocol and URL for enclave support.
|
||||
// For our purposes we need only set ce_option.enabled to true if not disabled.
|
||||
conn->ce_option.enabled = false;
|
||||
if ( !stricmp(value_str, "enabled" )) {
|
||||
if ( stricmp(value_str, "disabled" )) {
|
||||
conn->ce_option.enabled = true;
|
||||
}
|
||||
|
||||
|
@ -1200,7 +1203,7 @@ void ce_akv_str_set_func::func(_In_ connection_option const* option, _In_ zval*
|
|||
char *pValue = static_cast<char*>(sqlsrv_malloc(value_len + 1));
|
||||
memcpy_s(pValue, value_len + 1, value_str, value_len);
|
||||
pValue[value_len] = '\0'; // this makes sure there will be no trailing garbage
|
||||
|
||||
|
||||
// This will free the existing memory block before assigning the new pointer -- the user might set the value(s) more than once
|
||||
if (option->conn_option_key == SQLSRV_CONN_OPTION_KEYSTORE_PRINCIPAL_ID) {
|
||||
conn->ce_option.akv_id = pValue;
|
||||
|
@ -1262,10 +1265,10 @@ void access_token_set_func::func( _In_ connection_option const* option, _In_ zva
|
|||
}
|
||||
|
||||
const char* value_str = Z_STRVAL_P( value );
|
||||
|
||||
// The SQL_COPT_SS_ACCESS_TOKEN pre-connection attribute allows the use of an access token (in the format extracted from
|
||||
// an OAuth JSON response), obtained from Azure AD for authentication instead of username and password, and also
|
||||
// bypasses the negotiation and obtaining of an access token by the driver. To use an access token, set the
|
||||
|
||||
// The SQL_COPT_SS_ACCESS_TOKEN pre-connection attribute allows the use of an access token (in the format extracted from
|
||||
// an OAuth JSON response), obtained from Azure AD for authentication instead of username and password, and also
|
||||
// bypasses the negotiation and obtaining of an access token by the driver. To use an access token, set the
|
||||
// SQL_COPT_SS_ACCESS_TOKEN connection attribute to a pointer to an ACCESSTOKEN structure
|
||||
//
|
||||
// typedef struct AccessToken
|
||||
|
@ -1276,30 +1279,30 @@ void access_token_set_func::func( _In_ connection_option const* option, _In_ zva
|
|||
//
|
||||
// NOTE: The ODBC Driver version 13.1 only supports this authentication on Windows.
|
||||
//
|
||||
// A valid access token byte string must be expanded so that each byte is followed by a 0 padding byte,
|
||||
// A valid access token byte string must be expanded so that each byte is followed by a 0 padding byte,
|
||||
// similar to a UCS-2 string containing only ASCII characters
|
||||
//
|
||||
// See https://docs.microsoft.com/sql/connect/odbc/using-azure-active-directory#authenticating-with-an-access-token
|
||||
|
||||
size_t dataSize = 2 * value_len;
|
||||
|
||||
sqlsrv_malloc_auto_ptr<ACCESSTOKEN> accToken;
|
||||
|
||||
sqlsrv_malloc_auto_ptr<ACCESSTOKEN> accToken;
|
||||
accToken = reinterpret_cast<ACCESSTOKEN*>(sqlsrv_malloc(sizeof(ACCESSTOKEN) + dataSize));
|
||||
|
||||
ACCESSTOKEN *pAccToken = accToken.get();
|
||||
SQLSRV_ASSERT(pAccToken != NULL, "Something went wrong when trying to allocate memory for the access token.");
|
||||
|
||||
pAccToken->dataSize = dataSize;
|
||||
|
||||
|
||||
// Expand access token with padding bytes
|
||||
for (size_t i = 0, j = 0; i < dataSize; i += 2, j++) {
|
||||
pAccToken->data[i] = value_str[j];
|
||||
pAccToken->data[i+1] = 0;
|
||||
}
|
||||
|
||||
|
||||
core::SQLSetConnectAttr(conn, SQL_COPT_SS_ACCESS_TOKEN, reinterpret_cast<SQLPOINTER>(pAccToken), SQL_IS_POINTER);
|
||||
|
||||
// Save the pointer because SQLDriverConnect() will use it to make connection to the server
|
||||
|
||||
// Save the pointer because SQLDriverConnect() will use it to make connection to the server
|
||||
conn->azure_ad_access_token = pAccToken;
|
||||
accToken.transferred();
|
||||
}
|
||||
|
|
163
test/functional/pdo_sqlsrv/AE_v2_values.inc
Normal file
163
test/functional/pdo_sqlsrv/AE_v2_values.inc
Normal file
|
@ -0,0 +1,163 @@
|
|||
<?php
|
||||
include("MsSetup.inc");
|
||||
|
||||
$tableName = "aev2test";
|
||||
|
||||
// Names of the encryption keys, depending on whether we are using Windows
|
||||
// or AKV authentication (defined in MsSetup.inc). -enclave keys are enclave
|
||||
// enabled, -noenclave keys are not enclave enabled.
|
||||
// $targetKeys are the keys used for re-encrypting encrypted columns
|
||||
if ($keystore == 'win') {
|
||||
$keys = array("CEK-win-enclave", "CEK-win-noenclave");
|
||||
$targetKeys = array("CEK-win-enclave", "CEK-win-enclave2", "CEK-win-noenclave", "CEK-win-noenclave2");
|
||||
} elseif ($keystore == 'akv') {
|
||||
$keys = array("CEK-akv-enclave", "CEK-akv-noenclave");
|
||||
$targetKeys = array("CEK-akv-enclave", "CEK-akv-enclave2", "CEK-akv-noenclave", "CEK-akv-noenclave2");
|
||||
}
|
||||
|
||||
// $targetTypes are the encryption types used for re-encrypting encrypted columns
|
||||
$encryptionTypes = array("Randomized", "Deterministic");
|
||||
$targetTypes = array("Deterministic", "Randomized");
|
||||
|
||||
// Length of the string-type columns. $slength is length as a string instead of integer
|
||||
$length = 64;
|
||||
$slength = '64';
|
||||
|
||||
// Testing the following data types.
|
||||
// TODO: Add binary string types and fix smalldatetime issues.
|
||||
$dataTypes = array('integer',
|
||||
'bigint',
|
||||
'smallint',
|
||||
'tinyint',
|
||||
'bit',
|
||||
'float',
|
||||
'real',
|
||||
'numeric',
|
||||
'char',
|
||||
'nchar',
|
||||
'varchar',
|
||||
'nvarchar',
|
||||
'varchar(max)',
|
||||
'nvarchar(max)',
|
||||
//'binary',
|
||||
//'varbinary',
|
||||
//'varbinary(max)',
|
||||
'date',
|
||||
'time',
|
||||
'datetime',
|
||||
'datetime2',
|
||||
'datetimeoffset',
|
||||
//'smalldatetime',
|
||||
);
|
||||
|
||||
// Construct the array of column names. Two columns for each data type,
|
||||
// one encrypted (suffixed _AE) and one not encrypted.
|
||||
$colNames = array();
|
||||
$colNamesAE = array();
|
||||
|
||||
foreach ($dataTypes as $type) {
|
||||
$column = str_replace(array("(", ",", ")"), array("_", "_", ""), $type);
|
||||
$colNames[$type] = "c_".$column;
|
||||
$colNamesAE[$type] = "c_".$column."_AE";
|
||||
}
|
||||
|
||||
// The test data below is a mixture of random data and edge cases
|
||||
$testValues = array();
|
||||
|
||||
// integers
|
||||
$testValues['integer'] = array(0,-1,1,2147483647,-2147483648,65536,-100000000,128,9);
|
||||
$testValues['bigint'] = array(9223372036854775807,-40,0,1,2147483647,-2147483648,65536,-100000000000000);
|
||||
$testValues['smallint'] = array(4,-4,-32768,-99,32767,-30000,-12,-1);
|
||||
$testValues['tinyint'] = array(2,0,255,254,99,101,100,32);
|
||||
$testValues['bit'] = array(1,1,0,0,0,0,1,0);
|
||||
|
||||
// floating point
|
||||
$testValues['float'] = array(3.14159,2.3e+12,-2.3e+12,2.23e-308,1,-1.79e+308,892.3098234234001,1.2);
|
||||
$testValues['real'] = array(3.14159,2.3e+12,-2.3e+12,1.18e-38,1,-3.4e+38,892.3098234234001,1.2);
|
||||
$testValues['numeric'] = array(-3.14159,1.003456789,45.6789,-0.000000001,987987.12345,-987987.12345,100000000000,-100000000000);
|
||||
|
||||
// dates and times
|
||||
$testValues['date'] = array('2010-01-31','0485-03-31','7825-07-23','9999-12-31','1956-02-27','2018-09-01','5401-11-02','1031-10-04');
|
||||
$testValues['time'] = array('12:40:40','08:14:54.3096','23:59:59.9999','01:00:34.0101','21:45:45.4545','00:23:45.6','17:48:00.0000','20:31:49.0001');
|
||||
$testValues['datetime2'] = array('9801-01-29 11:45:23.5092856','2384-12-31 12:40:12.5434323','1984-09-25 10:40:20.0909111','9999-12-31 23:59:59.999999',
|
||||
'1259-04-29 23:59:59.9999999','1748-09-21 17:48:54.723','3125-05-31 05:00:32.4','0001-01-01 00:00:00');
|
||||
$testValues['datetimeoffset'] = array('9801-01-29 11:45:23.5092856-12:45','0001-01-01 00:00:00-02:30','1984-09-25 10:40:20.0909111+03:00','1748-09-21 17:48:54.723-09:21',
|
||||
'4896-05-18 23:17:58.3-02:00','1657-08-04 18:14:27.4','2022-03-17 07:31:45.890342+09:30','1987-10-25 14:27:34.6320945-06:00');
|
||||
$testValues['datetime'] = array('9801-01-29 11:45:23.509','2384-12-31 12:40:12.543','1984-09-25 10:40:20.090','9999-12-31 23:59:59.997',
|
||||
'2753-04-29 23:59:59.997','1948-09-21 17:48:54.723','3125-05-31 05:00:32.4','2001-01-01 00:00:00');
|
||||
$testValues['smalldatetime'] = array('1998-06-13 04:00:30','1985-03-31 12:40:40','2025-07-23 05:00:32','1999-12-31 00:00:00',
|
||||
'1956-02-27 23:59:59','2018-09-01 14:35:30','2079-06-06 23:59:29.997','1931-10-04 19:52:21');
|
||||
|
||||
// strings, ascii and unicode
|
||||
$testValues['char'] = array('wvyxz', 'tposw', '%c@kj>5', 'fd4$_w@q^@!coe$7', 'abcd', 'ev72#x*fv=u$', '4rfg3sw', 'voi%###i<@@');
|
||||
$testValues['nchar'] = array('⽧㘎ⷅ㪋','af㋮ᶄḉㇼ៌ӗඣ','ኁ㵮ഖᅥ㪮ኸ⮊ߒᙵꇕ⯐គꉟफ़⻦ꈔꇼŞ','ꐷꬕ','㐯㩧㖃⺵㴰ڇལᧆ겴ꕕ겑וֹꔄ若㌉ᒵȅ㗉ꗅᡉ','ʭḪぅᾔᎀ㍏겶ꅫၞ㴉ᴳ㜞҂','','בּŬḛʼꃺꌖ㓵ꗛ᧽ഭწ社⾯㬄౧ຸฬ㐯ꋛ㗾');
|
||||
$testValues['varchar'] = array('gop093','*#$@@)%*$@!%','cio4*3do*$','zzz$a#l',' ','v#x%n!k&r@p$f^','6$gt?je#~','0x3dK#?');
|
||||
$testValues['nvarchar'] = array('ᾁẴ㔮㖖ୱܝ㐗㴴៸ழ᷂ᵄ葉អ㺓節','ӕᏵ൴ꔓὀ⾼','Ὡ','璉Džꖭ갪ụ⺭','Ӿϰᇬ㭡㇑ᵈᔆ⽹hᙎ՞ꦣ㧼ለͭ','Ĕ㬚㔈♠既','ꁈ ݫ','ꍆફⷌ㏽̗ૣܯ¢⽳㌭ゴᔓᅄѓⷉꘊⶮᏏᴗஈԋ≡ㄊହꂈ꓂ꑽრꖾŞ⽉걹ꩰോఫ㒧㒾㑷藍㵀ဲ更ꧥ');
|
||||
$testValues['varchar(max)'] = array('Q0H4@4E%v+ 3*Trx#>*r86-&d$VgjZ','AjEvVABur(A&Q@eG,A$3u"xAzl','z#dFd4z',
|
||||
'9Dvsg9B?7oktB@|OIqy<\K^\e|*7Y&yH31E-<.hQ:)g Jl`MQV>rdOhjG;B4wQ(WR[`l(pELt0FYu._T3+8tns!}Nq<i-Nqbu@]1<K{PS[SHSF(]G[G XPLlAUezBm^&qn^mK(&]ss6&yVxW_N_J5V*iKcgXyb+Hz:HS<9>rc1%n@|N|ik C@ 03a/ +H9mBq','SSs$Ie*{:D4;S]',' ','<\K^\e|*7Y&yH31E-<.hQ:','@Kg1Z6XTOgbt?CEJ|M^rkR_L4{1?l<e`N@');
|
||||
$testValues['nvarchar(max)'] = array('xᐕᛙᘡ','ퟅ㚶Ἢœäᑐï','ꐾɔᡧ㝚ஒŪᚔᘘښ곅սꕟքꀉᎠኇ','t9p4r5',
|
||||
'﹨ퟱꈽᕧු꧍ۡᢙⴖ㒘ᆾ겇ᅞ〱㝸㛾㕥গଜ㳸ꍍ匿ཋ㵔ﬠᄩ᧙ꖍᕿ㩴ఽᙿ','ⴠ⿃ᶺ͚ᎉ㵨㛢㌋㙤ᙘّᘷ㬡',
|
||||
'ᵄご︵ࣲꌤꏵퟰꖛᏠƢᵙꌵ㙈㜂琢㎯㪏㐵꒚㧶ᐁቴƯɋü㶌領㻡㉉걂ꈊㇷѼμꅲڧᶀƸڍ⭩㉩㛜ꆶ㕸ꁺꖁ㓫ޘ갧ᛄ㶋㘚ᐋꗡͭచ㖔፟ꐸ㱯ⵜᥰꃷᇂὥ㗍㚀ꀊጿἢઔܛ᎓Ե⅜㛵ྣᏝ༱⮢ΫÊ㕮⽹','繁Ɇʓӿꩭਸꆟꑇ㳋Ήᴝ㕨㰵ꇳ');
|
||||
|
||||
// binary
|
||||
$testValues['binary'] = array(0x3AD2BBC2, 720, 0xEED3A109F8F7745C, 0xD6C3E0E11A25F,0x4EACCEF38788F9,0xFFFFFFFFFFFFFFFF,6230974598,0x44E4A);
|
||||
$testValues['varbinary'] = array(58342,0xF3ED38AAC3CDC87759DE34B23C223CCDAB42109FBC888,0xE4300FF,0x000005ED309D3A45,0x06CADE379,804,0x00000000000000,0xD7209FFE4);
|
||||
$testValues['varbinary(max)'] = array(0xEF409CB33408, 0xD3EA762C78F,0,0xFFFFFFFFFFFFFFFFFFFFFFFFF,
|
||||
0x582D40EF3EB4E9762C5AA49D4E40C42CB4009ED3E75F890A2FD14BF495EFF5378A23BB782C4A40E1D0005DA3FE208A48C1F,
|
||||
0x38054,9230094389109,0xDDD4D88C4B80089D2E4A,0x88F8A8);
|
||||
|
||||
// The comparison operators to test
|
||||
$comparisons = array('=', '<', '>', '<=', '>=', '<>', '!<', '!>');
|
||||
|
||||
// Thresholds against which to use the comparison operators
|
||||
$thresholds = array('integer' => 0,
|
||||
'bigint' => 0,
|
||||
'smallint' => 1000,
|
||||
'tinyint' => 100,
|
||||
'bit' => 0,
|
||||
'float' => 1.2,
|
||||
'real' => -1.2,
|
||||
'numeric' => 45.6789,
|
||||
'char' => 'rstuv',
|
||||
'nchar' => '㊃ᾞਲ㨴꧶ꁚꅍ',
|
||||
'varchar' => '6$gt?je#~',
|
||||
'nvarchar' => 'ӕᏵ൴ꔓὀ⾼',
|
||||
'varchar(max)' => 'hijkl',
|
||||
'nvarchar(max)' => 'xᐕᛙᘡ',
|
||||
'binary' => 0x44E4A,
|
||||
'varbinary' => 0xE4300FF,
|
||||
'varbinary(max)' => 0xD3EA762C78F,
|
||||
'date' => '2010-01-31',
|
||||
'time' => '21:45:45.4545',
|
||||
'datetime' => '3125-05-31 05:00:32.4',
|
||||
'datetime2' => '2384-12-31 12:40:12.5434323',
|
||||
'datetimeoffset' => '1984-09-25 10:40:20.0909111+03:00',
|
||||
'smalldatetime' => '1998-06-13 04:00:30',
|
||||
);
|
||||
|
||||
// String patterns to test with LIKE
|
||||
$patterns = array('integer' => array('8', '48', '123'),
|
||||
'bigint' => array('000','7', '65536'),
|
||||
'smallint' => array('4','768','abc'),
|
||||
'tinyint' => array('9','0','25'),
|
||||
'bit' => array('0','1','100'),
|
||||
'float' => array('14159','.','E+','2.3','308'),
|
||||
'real' => array('30','.','e-','2.3','38'),
|
||||
'numeric' => array('0','0000','12345','abc','.'),
|
||||
'char' => array('w','@','x*fv=u$','e3'),
|
||||
'nchar' => array('af㋮','㐯ꋛ㗾','ꦣ㧼ለͭ','123'),
|
||||
'varchar' => array(' ','a','#','@@)'),
|
||||
'nvarchar' => array('ӕ','Ӿϰᇬ㭡','璉Džꖭ갪ụ⺭','更ꧥ','ꈔꇼŞ'),
|
||||
'varchar(max)' => array('A','|*7Y&','4z','@!@','AjE'),
|
||||
'nvarchar(max)' => array('t','㧶ᐁቴƯɋ','ᘷ㬡',' ','ꐾɔᡧ㝚'),
|
||||
'binary' => array('0x44E4A'),
|
||||
'varbinary' => array('0xE4300FF'),
|
||||
'varbinary(max)' => array('0xD3EA762C78F'),
|
||||
'date' => array('20','%','9-','04'),
|
||||
'time' => array('4545','.0','20:','12345',':'),
|
||||
'datetime' => array('997','12',':5','9999'),
|
||||
'datetime2' => array('3125-05-31 05:','.45','$f#','-29 ','0001'),
|
||||
'datetimeoffset' => array('+02','96',' ','5092856',':00'),
|
||||
'smalldatetime' => array('00','1999','abc',':','06'),
|
||||
);
|
||||
?>
|
|
@ -49,4 +49,6 @@ $AKVPassword = 'TARGET_AKV_PASSWORD'; // for use with KeyVaultPasswo
|
|||
$AKVClientID = 'TARGET_AKV_CLIENT_ID'; // for use with KeyVaultClientSecret
|
||||
$AKVSecret = 'TARGET_AKV_CLIENT_SECRET'; // for use with KeyVaultClientSecret
|
||||
|
||||
// for enclave computations
|
||||
$attestation = 'TARGET_ATTESTATION';
|
||||
?>
|
488
test/functional/pdo_sqlsrv/pdo_AE_functions.inc
Normal file
488
test/functional/pdo_sqlsrv/pdo_AE_functions.inc
Normal file
|
@ -0,0 +1,488 @@
|
|||
<?php
|
||||
|
||||
// Connect and clear the procedure cache
|
||||
function connect($server, $attestation_info)
|
||||
{
|
||||
include("MsSetup.inc");
|
||||
$options = "sqlsrv:Server=$server;Database=$databaseName;ColumnEncryption=$attestation_info";
|
||||
|
||||
if ($keystore == 'akv') {
|
||||
|
||||
$security_info = '';
|
||||
if ($AKVKeyStoreAuthentication == 'KeyVaultPassword') {
|
||||
$security_info .= ";KeyStoreAuthentication=$AKVKeyStoreAuthentication";
|
||||
$security_info .= ";KeyStorePrincipalId=$AKVPrincipalName";
|
||||
$security_info .= ";KeyStoreSecret=$AKVPassword";
|
||||
} elseif ($AKVKeyStoreAuthentication == 'KeyVaultClientSecret') {
|
||||
$security_info .= ";KeyStoreAuthentication=$AKVKeyStoreAuthentication";
|
||||
$security_info .= ";KeyStorePrincipalId=$AKVClientID";
|
||||
$security_info .= ";KeyStoreSecret=$AKVSecret";
|
||||
} else {
|
||||
die("Incorrect value for KeyStoreAuthentication keyword!\n");
|
||||
}
|
||||
|
||||
$options .= $security_info;
|
||||
}
|
||||
|
||||
$conn = new PDO($options, $uid, $pwd);
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
$conn->setAttribute(PDO::SQLSRV_ATTR_FETCHES_DATETIME_TYPE, true);
|
||||
|
||||
// Check that enclave computations are enabled
|
||||
// See https://docs.microsoft.com/en-us/sql/relational-databases/security/encryption/configure-always-encrypted-enclaves?view=sqlallproducts-allversions#configure-a-secure-enclave
|
||||
$query = "SELECT [name], [value], [value_in_use] FROM sys.configurations WHERE [name] = 'column encryption enclave type';";
|
||||
$stmt = $conn->query($query);
|
||||
$info = $stmt->fetch();
|
||||
if ($info['value'] != 1 or $info['value_in_use'] != 1) {
|
||||
die("Error: enclave computations are not enabled on the server!");
|
||||
}
|
||||
|
||||
// Free the encryption cache to avoid spurious 'operand type clash' errors
|
||||
$conn->exec("DBCC FREEPROCCACHE");
|
||||
|
||||
return $conn;
|
||||
}
|
||||
|
||||
// This CREATE TABLE query simply creates a non-encrypted table with
|
||||
// two columns for each data type side by side
|
||||
// This produces a query that looks like
|
||||
// CREATE TABLE aev2test2 (
|
||||
// c_integer integer,
|
||||
// c_integer_AE integer
|
||||
// )
|
||||
function constructCreateQuery($tableName, $dataTypes, $colNames, $colNamesAE, $slength)
|
||||
{
|
||||
$query = "CREATE TABLE ".$tableName." (\n ";
|
||||
foreach ($dataTypes as $type) {
|
||||
|
||||
if (dataTypeIsString($type)) {
|
||||
$query = $query.$colNames[$type]." ".$type."(".$slength."), \n ";
|
||||
$query = $query.$colNamesAE[$type]." ".$type."(".$slength."), \n ";
|
||||
} else {
|
||||
$query = $query.$colNames[$type]." ".$type.", \n ";
|
||||
$query = $query.$colNamesAE[$type]." ".$type.", \n ";
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the ", \n " from the end of the query or the comma will cause a syntax error
|
||||
$query = substr($query, 0, -7)."\n)";
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
// The ALTER TABLE query encrypts columns. Each ALTER COLUMN directive must
|
||||
// be preceded by ALTER TABLE. This query can be used to both encrypt plaintext
|
||||
// columns and to re-encrypt encrypted columns.
|
||||
// This produces a query that looks like
|
||||
// ALTER TABLE [dbo].[aev2test2]
|
||||
// ALTER COLUMN [c_integer_AE] integer
|
||||
// ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [CEK-win-enclave], ENCRYPTION_TYPE = Randomized, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NOT NULL
|
||||
// WITH
|
||||
// (ONLINE = ON); ALTER TABLE [dbo].[aev2test2]
|
||||
// ALTER COLUMN [c_bigint_AE] bigint
|
||||
// ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [CEK-win-enclave], ENCRYPTION_TYPE = Randomized, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NOT NULL
|
||||
// WITH
|
||||
// (ONLINE = ON); ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE;
|
||||
function constructAlterQuery($tableName, $colNames, $dataTypes, $key, $encryptionType, $slength)
|
||||
{
|
||||
$query = '';
|
||||
foreach ($dataTypes as $dataType) {
|
||||
|
||||
$plength = dataTypeIsString($dataType) ? "(".$slength.")" : "";
|
||||
$collate = dataTypeNeedsCollate($dataType) ? " COLLATE Latin1_General_BIN2" : "";
|
||||
$query = $query." ALTER TABLE [dbo].[".$tableName."]
|
||||
ALTER COLUMN [".$colNames[$dataType]."] ".$dataType.$plength." ".$collate."
|
||||
ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [".$key."], ENCRYPTION_TYPE = ".$encryptionType.", ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NOT NULL
|
||||
WITH
|
||||
(ONLINE = ON);";
|
||||
}
|
||||
|
||||
$query = $query." ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE;";
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
// This CREATE TABLE query creates a table with two columns for
|
||||
// each data type side by side, one plaintext and one encrypted
|
||||
// This produces a query that looks like
|
||||
// CREATE TABLE aev2test2 (
|
||||
// c_integer integer NULL,
|
||||
// c_integer_AE integer
|
||||
// COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [CEK-win-enclave], ENCRYPTION_TYPE = Randomized, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL
|
||||
// )
|
||||
function constructAECreateQuery($tableName, $dataTypes, $colNames, $colNamesAE, $slength, $key, $encryptionType)
|
||||
{
|
||||
$query = "CREATE TABLE ".$tableName." (\n ";
|
||||
|
||||
foreach ($dataTypes as $type) {
|
||||
|
||||
$collate = dataTypeNeedsCollate($type) ? " COLLATE Latin1_General_BIN2" : "";
|
||||
|
||||
if (dataTypeIsString($type)) {
|
||||
$query = $query.$colNames[$type]." ".$type."(".$slength.") NULL, \n ";
|
||||
$query = $query.$colNamesAE[$type]." ".$type."(".$slength.") \n ";
|
||||
$query = $query." ".$collate." ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [".$key."], ENCRYPTION_TYPE = ".$encryptionType.", ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL,\n ";
|
||||
} else {
|
||||
$query = $query.$colNames[$type]." ".$type." NULL, \n ";
|
||||
$query = $query.$colNamesAE[$type]." ".$type." \n ";
|
||||
$query = $query." ".$collate." ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [".$key."], ENCRYPTION_TYPE = ".$encryptionType.", ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL,\n ";
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the ",\n " from the end of the query or the comma will cause a syntax error
|
||||
$query = substr($query, 0, -6)."\n)";
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
// The INSERT query for the table
|
||||
function constructInsertQuery($tableName, &$dataTypes, &$colNames, &$colNamesAE)
|
||||
{
|
||||
$queryTypes = "(";
|
||||
$valuesString = "VALUES (";
|
||||
|
||||
foreach ($dataTypes as $type) {
|
||||
$colName1 = $colNames[$type].", ";
|
||||
$colName2 = $colNamesAE[$type].", ";
|
||||
$queryTypes .= $colName1;
|
||||
$queryTypes .= $colName2;
|
||||
$valuesString .= "?, ?, ";
|
||||
}
|
||||
|
||||
// Remove the ", " from the end of the query or the comma will cause a syntax error
|
||||
$queryTypes = substr($queryTypes, 0, -2).")";
|
||||
$valuesString = substr($valuesString, 0, -2).")";
|
||||
|
||||
$insertQuery = "INSERT INTO $tableName ".$queryTypes." ".$valuesString;
|
||||
|
||||
return $insertQuery;
|
||||
}
|
||||
|
||||
function insertValues($conn, $insertQuery, $dataTypes, $testValues)
|
||||
{
|
||||
for ($v = 0; $v < sizeof($testValues['bigint']); ++$v) {
|
||||
$insertValues = array();
|
||||
|
||||
foreach ($dataTypes as $type) {
|
||||
$insertValues[] = $testValues[$type][$v];
|
||||
$insertValues[] = $testValues[$type][$v];
|
||||
}
|
||||
|
||||
// Insert the data using PDO::prepare()
|
||||
try {
|
||||
$stmt = $conn->prepare($insertQuery);
|
||||
$stmt->execute($insertValues);
|
||||
} catch (PDOException $error) {
|
||||
print_r($error);
|
||||
die("Inserting values in encrypted table failed\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// compareResults checks that the results between the encrypted and non-encrypted
|
||||
// columns are identical if statement execution succeeds. If statement execution
|
||||
// fails, this function checks for the correct error.
|
||||
// Arguments:
|
||||
// statement $AEstmt: Prepared statement fetching encrypted data
|
||||
// statement $nonAEstmt: Prepared statement fetching non-encrypted data
|
||||
// string $key: Name of the encryption key
|
||||
// string $encryptionType: Type of encryption, randomized or deterministic
|
||||
// string $attestation: Type of attestation - 'correct', 'enabled', or 'wrongurl'
|
||||
// string $comparison: Comparison operator
|
||||
// string $type: Data type the comparison is operating on
|
||||
function compareResults($AEstmt, $nonAEstmt, $key, $encryptionType, $attestation, $comparison='', $type='')
|
||||
{
|
||||
try {
|
||||
$nonAEstmt->execute();
|
||||
} catch(Exception $error) {
|
||||
print_r($error);
|
||||
die("Executing non-AE statement failed!\n");
|
||||
}
|
||||
|
||||
try {
|
||||
$AEstmt->execute();
|
||||
} catch(Exception $error) {
|
||||
if ($attestation == 'enabled') {
|
||||
if ($encryptionType == 'Deterministic') {
|
||||
if ($comparison == '=') {
|
||||
print_r($error);
|
||||
die("Equality comparison failed for deterministic encryption!\n");
|
||||
} else {
|
||||
$e = $error->errorInfo;
|
||||
checkErrors($e, array('42000', '33277'));
|
||||
}
|
||||
} elseif (isEnclaveEnabled($key)) {
|
||||
$e = $error->errorInfo;
|
||||
checkErrors($e, array('42000', '33546'));
|
||||
} elseif (!isEnclaveEnabled($key)) {
|
||||
$e = $error->errorInfo;
|
||||
checkErrors($e, array('42000', '33277'));
|
||||
} else {
|
||||
print_r($error);
|
||||
die("AE statement execution failed when it shouldn't!");
|
||||
}
|
||||
} elseif ($attestation == 'wrongurl') {
|
||||
if ($encryptionType == 'Deterministic') {
|
||||
if ($comparison == '=') {
|
||||
$e = $error->errorInfo;
|
||||
die("Equality comparison failed for deterministic encryption!\n");
|
||||
} else {
|
||||
$e = $error->errorInfo;
|
||||
checkErrors($e, array('42000', '33277'));
|
||||
}
|
||||
} elseif (isEnclaveEnabled($key)) {
|
||||
$e = $error->errorInfo;
|
||||
checkErrors($e, array('CE405', '0'));
|
||||
} elseif (!isEnclaveEnabled($key)) {
|
||||
$e = $error->errorInfo;
|
||||
checkErrors($e, array('42000', '33277'));
|
||||
} else {
|
||||
print_r($error);
|
||||
die("AE statement execution failed when it shouldn't!");
|
||||
}
|
||||
} elseif ($attestation == 'correct') {
|
||||
if (!isEnclaveEnabled($key) and $encryptionType == 'Randomized') {
|
||||
$e = $error->errorInfo;
|
||||
checkErrors($e, array('42000', '33277'));
|
||||
} elseif ($encryptionType == 'Deterministic') {
|
||||
if ($comparison == '=') {
|
||||
print_r($error);
|
||||
die("Equality comparison failed for deterministic encryption!\n");
|
||||
} else {
|
||||
$e = $error->errorInfo;
|
||||
checkErrors($e, array('42000', '33277'));
|
||||
}
|
||||
} else {
|
||||
print_r($error);
|
||||
die("Comparison failed for correct attestation when it shouldn't have!\n");
|
||||
}
|
||||
} else {
|
||||
print_r($error);
|
||||
die("Unexpected error occurred in compareResults!\n");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$AEres = $AEstmt->fetchAll(PDO::FETCH_NUM);
|
||||
$nonAEres = $nonAEstmt->fetchAll(PDO::FETCH_NUM);
|
||||
$AEcount = count($AEres);
|
||||
$nonAEcount = count($nonAEres);
|
||||
|
||||
if ($type == 'char' or $type == 'nchar') {
|
||||
// char and nchar may not return the same results - at this point
|
||||
// we've verified that statement execution works so just return
|
||||
// TODO: Check if this bug is fixed and if so, remove this if block
|
||||
return;
|
||||
} elseif ($AEcount > $nonAEcount) {
|
||||
print_r("Too many AE results for operation $comparison and data type $type!\n");
|
||||
print_r($AEres);
|
||||
print_r($nonAEres);
|
||||
} elseif ($AEcount < $nonAEcount) {
|
||||
print_r("Too many non-AE results for operation $comparison and data type $type!\n");
|
||||
print_r($AEres);
|
||||
print_r($nonAEres);
|
||||
} else {
|
||||
if ($AEcount != 0) {
|
||||
$i = 0;
|
||||
foreach ($AEres as $AEr) {
|
||||
if ($AEr[0] != $nonAEres[$i][0]) {
|
||||
print_r("AE and non-AE results are different for operation $comparison and data type $type! For field $i, got AE result ".$AEres[$i][0]." and non-AE result ".$nonAEres[$i][0]."\n");
|
||||
}
|
||||
++$i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// testCompare selects based on a comparison in the WHERE clause and compares
|
||||
// the results between encrypted and non-encrypted columns, checking that the
|
||||
// results are identical
|
||||
// Arguments:
|
||||
// resource $conn: The connection
|
||||
// string $tableName: Table name
|
||||
// array $comparisons: Comparison operations from AE_v2_values.inc
|
||||
// array $dataTypes: Data types from AE_v2_values.inc
|
||||
// array $colNames: Column names
|
||||
// array $thresholds: Values to use comparison operators against, from AE_v2_values.inc
|
||||
// string $key: Name of the encryption key
|
||||
// string $encryptionType: Type of encryption, randomized or deterministic
|
||||
// string $attestation: Type of attestation - 'correct', 'enabled', or 'wrongurl'
|
||||
function testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $thresholds, $key, $encryptionType, $attestation)
|
||||
{
|
||||
foreach ($comparisons as $comparison) {
|
||||
foreach ($dataTypes as $type) {
|
||||
|
||||
// Unicode operations with AE require the Latin1_General_BIN2
|
||||
// collation. If the COLLATE clause is left out, we get different
|
||||
// results between the encrypted and non-encrypted columns (probably
|
||||
// because the collation was only changed in the encryption query).
|
||||
$string = dataTypeIsStringMax($type);
|
||||
$collate = $string ? " COLLATE Latin1_General_BIN2" : "";
|
||||
$unicode = dataTypeIsUnicode($type);
|
||||
$PDOType = getPDOType($type);
|
||||
|
||||
$AEQuery = "SELECT ".$colNames[$type]."_AE FROM $tableName WHERE ".$colNames[$type]."_AE ".$comparison." ?".$collate;
|
||||
$nonAEQuery = "SELECT ".$colNames[$type]." FROM $tableName WHERE ".$colNames[$type]." ".$comparison." ?".$collate;
|
||||
|
||||
try {
|
||||
$AEstmt = $conn->prepare($AEQuery);
|
||||
$AEstmt->bindParam(1, $thresholds[$type], $PDOType);
|
||||
$nonAEstmt = $conn->prepare($nonAEQuery);
|
||||
$nonAEstmt->bindParam(1, $thresholds[$type], $PDOType);
|
||||
} catch (PDOException $error) {
|
||||
print_r($error);
|
||||
die("Preparing/binding statements for comparison failed");
|
||||
}
|
||||
|
||||
compareResults($AEstmt, $nonAEstmt, $key, $encryptionType, $attestation, $comparison, $type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// testPatternMatch selects based on a pattern in the WHERE clause and compares
|
||||
// the results between encrypted and non-encrypted columns, checking that the
|
||||
// results are identical
|
||||
// Arguments:
|
||||
// resource $conn: The connection
|
||||
// string $tableName: Table name
|
||||
// array $patterns: Patterns to match against, from AE_v2_values.inc
|
||||
// array $dataTypes: Data types from AE_v2_values.inc
|
||||
// array $colNames: Column names
|
||||
// string $key: Name of the encryption key
|
||||
// string $encryptionType: Type of encryption, randomized or deterministic
|
||||
// string $attestation: Type of attestation - 'correct', 'enabled', or 'wrongurl'
|
||||
function testPatternMatch($conn, $tableName, $patterns, $dataTypes, $colNames, $key, $encryptionType, $attestation)
|
||||
{
|
||||
foreach ($dataTypes as $type) {
|
||||
|
||||
// TODO: Pattern matching doesn't work in AE for non-string types
|
||||
// without an explicit cast
|
||||
if (!dataTypeIsStringMax($type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($patterns[$type] as $pattern) {
|
||||
|
||||
$patternArray = array($pattern,
|
||||
$pattern."%",
|
||||
"%".$pattern,
|
||||
"%".$pattern."%",
|
||||
);
|
||||
|
||||
foreach ($patternArray as $spattern) {
|
||||
|
||||
// Unicode operations with AE require the Latin1_General_BIN2
|
||||
// collation. If the COLLATE clause is left out, we get different
|
||||
// results between the encrypted and non-encrypted columns (probably
|
||||
// because the collation was only changed in the encryption query).
|
||||
$unicode = dataTypeIsUnicode($type);
|
||||
$collate = $unicode ? " COLLATE Latin1_General_BIN2" : "";
|
||||
$PDOType = getPDOType($type);
|
||||
|
||||
$AEQuery = "SELECT ".$colNames[$type]."_AE FROM $tableName WHERE ".$colNames[$type]."_AE LIKE ?".$collate;
|
||||
$nonAEQuery = "SELECT ".$colNames[$type]." FROM $tableName WHERE ".$colNames[$type]." LIKE ?".$collate;
|
||||
|
||||
try {
|
||||
$AEstmt = $conn->prepare($AEQuery);
|
||||
$AEstmt->bindParam(1, $spattern, $PDOType);
|
||||
$nonAEstmt = $conn->prepare($nonAEQuery);
|
||||
$nonAEstmt->bindParam(1, $spattern, $PDOType);
|
||||
} catch (PDOException $error) {
|
||||
print_r($error);
|
||||
die("Preparing/binding statements for comparison failed");
|
||||
}
|
||||
|
||||
compareResults($AEstmt, $nonAEstmt, $key, $encryptionType, $attestation, $pattern, $type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkErrors($errors, ...$codes)
|
||||
{
|
||||
$codeFound = false;
|
||||
|
||||
foreach ($codes as $code) {
|
||||
if ($code[0]==$errors[0] and $code[1]==$errors[1]) {
|
||||
$codeFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($codeFound == false) {
|
||||
echo "Error: ";
|
||||
print_r($errors);
|
||||
echo "\nExpected: ";
|
||||
print_r($codes);
|
||||
echo "\n";
|
||||
die("Error code not found.\n");
|
||||
}
|
||||
}
|
||||
|
||||
function isEnclaveEnabled($key)
|
||||
{
|
||||
return (strpos($key, '-enclave') !== false);
|
||||
}
|
||||
|
||||
function dataTypeIsString($dataType)
|
||||
{
|
||||
return (in_array($dataType, ["binary", "varbinary", "char", "nchar", "varchar", "nvarchar"]));
|
||||
}
|
||||
|
||||
function dataTypeIsStringMax($dataType)
|
||||
{
|
||||
return (in_array($dataType, ["binary", "varbinary", "char", "nchar", "varchar", "nvarchar", "varchar(max)", "nvarchar(max)"]));
|
||||
}
|
||||
|
||||
function dataTypeNeedsCollate($dataType)
|
||||
{
|
||||
return (in_array($dataType, ["char", "nchar", "varchar", "nvarchar", "varchar(max)", "nvarchar(max)"]));
|
||||
}
|
||||
|
||||
function dataTypeIsUnicode($dataType)
|
||||
{
|
||||
return (in_array($dataType, ["nchar", "nvarchar", "nvarchar(max)"]));
|
||||
}
|
||||
|
||||
function getPDOType($type)
|
||||
{
|
||||
switch($type) {
|
||||
case "bigint":
|
||||
case "integer":
|
||||
case "smallint":
|
||||
case "tinyint":
|
||||
return PDO::PARAM_INT;
|
||||
case "bit":
|
||||
return PDO::PARAM_BOOL;
|
||||
case "real":
|
||||
case "float":
|
||||
case "double":
|
||||
case "numeric":
|
||||
case "time":
|
||||
case "date":
|
||||
case "datetime2":
|
||||
case "datetime":
|
||||
case "datetimeoffset":
|
||||
case "smalldatetime":
|
||||
case "money":
|
||||
case "smallmoney";
|
||||
case "xml":
|
||||
case "uniqueidentifier":
|
||||
case "char":
|
||||
case "varchar":
|
||||
case "varchar(max)":
|
||||
case "nchar":
|
||||
case "nvarchar":
|
||||
case "nvarchar(max)":
|
||||
return PDO::PARAM_STR;
|
||||
case "binary":
|
||||
case "varbinary":
|
||||
case "varbinary(max)":
|
||||
return PDO::PARAM_LOB;
|
||||
default:
|
||||
die("Case is missing for $type type in getPDOType.\n");
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
93
test/functional/pdo_sqlsrv/pdo_aev2_ce_enabled.phpt
Normal file
93
test/functional/pdo_sqlsrv/pdo_aev2_ce_enabled.phpt
Normal file
|
@ -0,0 +1,93 @@
|
|||
--TEST--
|
||||
Try re-encrypting a table with ColumnEncryption set to 'enabled', which should fail.
|
||||
--DESCRIPTION--
|
||||
This test cycles through $encryptionTypes and $keys, creating an encrypted table
|
||||
each time, then cycles through $targetTypes and $targetKeys to try re-encrypting
|
||||
the table with different combinations of enclave-enabled and non-enclave keys
|
||||
and encryption types.
|
||||
The sequence of operations is the following:
|
||||
1. Connect with correct attestation information.
|
||||
2. Create an encrypted table with two columns for each AE-supported data type, one encrypted and one not encrypted.
|
||||
3. Insert some data.
|
||||
4. Disconnect and reconnect with ColumnEncryption set to 'enabled'.
|
||||
5. Test comparison and pattern matching by comparing the results for the encrypted and non-encrypted columns.
|
||||
Equality should work with deterministic encryption as in AE v1, but other computations should fail.
|
||||
6. Try re-encrypting the table. This should fail.
|
||||
--SKIPIF--
|
||||
<?php require("skipif_not_hgs.inc"); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
require_once("AE_v2_values.inc");
|
||||
require_once("pdo_AE_functions.inc");
|
||||
|
||||
$initialAttestation = $attestation;
|
||||
|
||||
// Create a table for each key and encryption type, re-encrypt using each
|
||||
// combination of target key and target encryption
|
||||
foreach ($keys as $key) {
|
||||
foreach ($encryptionTypes as $encryptionType) {
|
||||
|
||||
// $count is used to ensure we only run testCompare and
|
||||
// testPatternMatch once for the initial table
|
||||
$count = 0;
|
||||
foreach ($targetKeys as $targetKey) {
|
||||
foreach ($targetTypes as $targetType) {
|
||||
|
||||
$conn = connect($server, $initialAttestation);
|
||||
|
||||
// Create an encrypted table
|
||||
$createQuery = constructAECreateQuery($tableName, $dataTypes, $colNames, $colNamesAE, $slength, $key, $encryptionType);
|
||||
$insertQuery = constructInsertQuery($tableName, $dataTypes, $colNames, $colNamesAE);
|
||||
|
||||
try {
|
||||
$stmt = $conn->query("DROP TABLE IF EXISTS $tableName");
|
||||
$stmt = $conn->query($createQuery);
|
||||
} catch(Exception $error) {
|
||||
print_r($error);
|
||||
die("Creating an encrypted table failed when it shouldn't have!\n");
|
||||
}
|
||||
|
||||
insertValues($conn, $insertQuery, $dataTypes, $testValues);
|
||||
unset($conn);
|
||||
|
||||
// Reconnect with ColumnEncryption set to 'enabled'
|
||||
$newAttestation = 'enabled';
|
||||
$conn = connect($server, $newAttestation);
|
||||
|
||||
if ($count == 0) {
|
||||
testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $thresholds, $key, $encryptionType, 'enabled');
|
||||
testPatternMatch($conn, $tableName, $patterns, $dataTypes, $colNames, $key, $encryptionType, 'enabled');
|
||||
}
|
||||
++$count;
|
||||
|
||||
if ($key == $targetKey and $encryptionType == $targetType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$alterQuery = constructAlterQuery($tableName, $colNamesAE, $dataTypes, $targetKey, $targetType, $slength);
|
||||
|
||||
try {
|
||||
$stmt = $conn->query($alterQuery);
|
||||
|
||||
// Query should fail and trigger catch block before getting here
|
||||
die("Encrypting should have failed with key $targetKey and encryption type $targetType\n");
|
||||
} catch (PDOException $error) {
|
||||
if (!isEnclaveEnabled($key) or !isEnclaveEnabled($targetKey)) {
|
||||
$e = $error->errorInfo;
|
||||
checkErrors($e, array('42000', '33543'));
|
||||
} else {
|
||||
$e = $error->errorInfo;
|
||||
checkErrors($e, array('42000', '33546'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "Done.\n";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done.
|
136
test/functional/pdo_sqlsrv/pdo_aev2_encrypt_plaintext.phpt
Normal file
136
test/functional/pdo_sqlsrv/pdo_aev2_encrypt_plaintext.phpt
Normal file
|
@ -0,0 +1,136 @@
|
|||
--TEST--
|
||||
Test rich computations and in-place encryption of plaintext with AE v2.
|
||||
--DESCRIPTION--
|
||||
This test cycles through $encryptionTypes and $keys, creating a plaintext table
|
||||
each time, then trying to encrypt it with different combinations of enclave-enabled and non-enclave keys
|
||||
and encryption types. It then cycles through $targetTypes and $targetKeys to try re-encrypting
|
||||
the table with different target combinations of enclave-enabled and non-enclave keys
|
||||
and encryption types.
|
||||
The sequence of operations is the following:
|
||||
1. Create a table in plaintext with two columns for each AE-supported data type.
|
||||
2. Insert some data in plaintext.
|
||||
3. Encrypt one column for each data type.
|
||||
4. Perform rich computations on each AE-enabled column (comparisons and pattern matching) and compare the result
|
||||
to the same query on the corresponding non-AE column for each data type.
|
||||
5. Ensure the two results are the same.
|
||||
6. Re-encrypt the table using new key and/or encryption type.
|
||||
7. Compare computations as in 4. above.
|
||||
--SKIPIF--
|
||||
<?php require("skipif_not_hgs.inc"); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
require_once("AE_v2_values.inc");
|
||||
require_once("pdo_AE_functions.inc");
|
||||
|
||||
$initialAttestation = $attestation;
|
||||
|
||||
// Create a table for each key and encryption type, re-encrypt using each
|
||||
// combination of target key and target encryption
|
||||
foreach ($keys as $key) {
|
||||
foreach ($encryptionTypes as $encryptionType) {
|
||||
|
||||
// $count is used to ensure we only run testCompare and
|
||||
// testPatternMatch once for the initial table
|
||||
$count = 0;
|
||||
$conn = connect($server, $attestation);
|
||||
|
||||
foreach ($targetKeys as $targetKey) {
|
||||
foreach ($targetTypes as $targetType) {
|
||||
|
||||
// Free the encryption cache to avoid spurious 'operand type clash' errors
|
||||
$conn->query("DBCC FREEPROCCACHE");
|
||||
|
||||
// Create and populate a non-encrypted table
|
||||
$createQuery = constructCreateQuery($tableName, $dataTypes, $colNames, $colNamesAE, $slength);
|
||||
$insertQuery = constructInsertQuery($tableName, $dataTypes, $colNames, $colNamesAE);
|
||||
|
||||
try {
|
||||
$stmt = $conn->query("DROP TABLE IF EXISTS $tableName");
|
||||
$stmt = $conn->query($createQuery);
|
||||
} catch(Exception $error) {
|
||||
print_r($error);
|
||||
die("Creating table failed when it shouldn't have!\n");
|
||||
}
|
||||
|
||||
insertValues($conn, $insertQuery, $dataTypes, $testValues);
|
||||
|
||||
if ($count == 0) {
|
||||
// Split the data type array, because for some reason we get an error
|
||||
// if the query is too long (>2000 characters)
|
||||
// TODO: This is a known issue, follow up on it.
|
||||
$splitDataTypes = array_chunk($dataTypes, 5);
|
||||
foreach ($splitDataTypes as $split) {
|
||||
$alterQuery = constructAlterQuery($tableName, $colNamesAE, $split, $key, $encryptionType, $slength);
|
||||
$encryptionFailed = false;
|
||||
|
||||
try {
|
||||
$stmt = $conn->query($alterQuery);
|
||||
if (!isEnclaveEnabled($key)) {
|
||||
die("Encrypting should have failed with key $key and encryption type $encryptionType\n");
|
||||
}
|
||||
} catch (PDOException $error) {
|
||||
if (!isEnclaveEnabled($key)) {
|
||||
$e = $error->errorInfo;
|
||||
checkErrors($e, array('42000', '33543'));
|
||||
$encryptionFailed = true;
|
||||
continue;
|
||||
} else {
|
||||
print_r($error);
|
||||
die("Encrypting failed when it shouldn't have!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($encryptionFailed) continue;
|
||||
|
||||
if ($count == 0) {
|
||||
testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $thresholds, $key, $encryptionType, 'correct');
|
||||
testPatternMatch($conn, $tableName, $patterns, $dataTypes, $colNames, $key, $encryptionType, 'correct');
|
||||
}
|
||||
++$count;
|
||||
|
||||
if ($key == $targetKey and $encryptionType == $targetType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try re-encrypting the table
|
||||
foreach ($splitDataTypes as $split) {
|
||||
$alterQuery = constructAlterQuery($tableName, $colNamesAE, $split, $targetKey, $targetType, $slength);
|
||||
$encryptionFailed = false;
|
||||
|
||||
try {
|
||||
$stmt = $conn->query($alterQuery);
|
||||
if (!isEnclaveEnabled($targetKey)) {
|
||||
die("Encrypting should have failed with key $targetKey and encryption type $targetType\n");
|
||||
}
|
||||
} catch (Exception $error) {
|
||||
if (!isEnclaveEnabled($targetKey)) {
|
||||
$e = $error->errorInfo;
|
||||
checkErrors($e, array('42000', '33543'));
|
||||
$encryptionFailed = true;
|
||||
continue;
|
||||
} else {
|
||||
print_r($error);
|
||||
die("Encrypting failed when it shouldn't have!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($encryptionFailed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $thresholds, $targetKey, $targetType, 'correct');
|
||||
testPatternMatch($conn, $tableName, $patterns, $dataTypes, $colNames, $targetKey, $targetType, 'correct');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "Done.\n";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done.
|
60
test/functional/pdo_sqlsrv/pdo_aev2_keywords.phpt
Normal file
60
test/functional/pdo_sqlsrv/pdo_aev2_keywords.phpt
Normal file
|
@ -0,0 +1,60 @@
|
|||
--TEST--
|
||||
Test various settings for the ColumnEncryption keyword.
|
||||
--DESCRIPTION--
|
||||
For AE v2, the Column Encryption keyword must be set to [protocol]/[attestation URL].
|
||||
If [protocol] is wrong, connection should fail; if the URL is wrong, connection
|
||||
should succeed. This test sets ColumnEncryption to three values:
|
||||
1. Random nonsense, which is interpreted as an incorrect protocol
|
||||
so connection should fail.
|
||||
2. Incorrect protocol with a correct attestation URL, connection should fail.
|
||||
3. Correct protocol and incorrect URL, connection should succeed.
|
||||
--SKIPIF--
|
||||
<?php require("skipif_not_hgs.inc"); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
require_once("AE_v2_values.inc");
|
||||
require_once("pdo_AE_functions.inc");
|
||||
|
||||
// Test with random nonsense. Connection should fail.
|
||||
$options = "sqlsrv:Server=$server;database=$databaseName;ColumnEncryption=xyz";
|
||||
|
||||
try {
|
||||
$conn = new PDO($options, $uid, $pwd);
|
||||
die("Connection should have failed!\n");
|
||||
} catch(PDOException $error) {
|
||||
$e = $error->errorInfo;
|
||||
checkErrors($e, array('CE400', '0'));
|
||||
}
|
||||
|
||||
// Test with incorrect protocol and good attestation URL. Connection should fail.
|
||||
// Insert a rogue 'x' into the protocol part of the attestation.
|
||||
$comma = strpos($attestation, ',');
|
||||
$badProtocol = substr_replace($attestation, 'x', $comma, 0);
|
||||
$options = "sqlsrv:Server=$server;database=$databaseName;ColumnEncryption=$badProtocol";
|
||||
|
||||
try {
|
||||
$conn = new PDO($options, $uid, $pwd);
|
||||
die("Connection should have failed!\n");
|
||||
} catch(Exception $error) {
|
||||
$e = $error->errorInfo;
|
||||
checkErrors($e, array('CE400', '0'));
|
||||
}
|
||||
|
||||
// Test with good protocol and incorrect attestation URL. Connection should succeed
|
||||
// because the URL is only checked when an enclave computation is attempted.
|
||||
$badURL = substr_replace($attestation, 'x', $comma+1, 0);
|
||||
$options = "sqlsrv:Server=$server;database=$databaseName;ColumnEncryption=$badURL";
|
||||
|
||||
try {
|
||||
$conn = new PDO($options, $uid, $pwd);
|
||||
} catch(Exception $error) {
|
||||
print_r($error);
|
||||
die("Connecting with a bad attestation URL should have succeeded!\n");
|
||||
}
|
||||
|
||||
echo "Done.\n";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done.
|
109
test/functional/pdo_sqlsrv/pdo_aev2_reencrypt_encrypted.phpt
Normal file
109
test/functional/pdo_sqlsrv/pdo_aev2_reencrypt_encrypted.phpt
Normal file
|
@ -0,0 +1,109 @@
|
|||
--TEST--
|
||||
Test rich computations and in place re-encryption with AE v2.
|
||||
--DESCRIPTION--
|
||||
This test cycles through $encryptionTypes and $keys, creating an encrypted table
|
||||
each time, then cycles through $targetTypes and $targetKeys to try re-encrypting
|
||||
the table with different combinations of enclave-enabled and non-enclave keys
|
||||
and encryption types.
|
||||
The sequence of operations is the following:
|
||||
1. Create an encrypted table with two columns for each AE-supported data type, one encrypted and one not encrypted.
|
||||
2. Insert some data.
|
||||
3. Perform rich computations on each AE-enabled column (comparisons and pattern matching) and compare the result
|
||||
to the same query on the corresponding non-AE column for each data type.
|
||||
4. Ensure the two results are the same.
|
||||
5. Re-encrypt the table using new key and/or encryption type.
|
||||
6. Compare computations as in 4. above.
|
||||
--SKIPIF--
|
||||
<?php require("skipif_not_hgs.inc"); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
require_once("AE_v2_values.inc");
|
||||
require_once("pdo_AE_functions.inc");
|
||||
|
||||
$initialAttestation = $attestation;
|
||||
|
||||
// Create a table for each key and encryption type, re-encrypt using each
|
||||
// combination of target key and target encryption
|
||||
foreach ($keys as $key) {
|
||||
foreach ($encryptionTypes as $encryptionType) {
|
||||
|
||||
// $count is used to ensure we only run testCompare and
|
||||
// testPatternMatch once for the initial table
|
||||
$count = 0;
|
||||
$conn = connect($server, $attestation);
|
||||
|
||||
foreach ($targetKeys as $targetKey) {
|
||||
foreach ($targetTypes as $targetType) {
|
||||
|
||||
// Free the encryption cache to avoid spurious 'operand type clash' errors
|
||||
$conn->query("DBCC FREEPROCCACHE");
|
||||
|
||||
// Create an encrypted table
|
||||
$createQuery = constructAECreateQuery($tableName, $dataTypes, $colNames, $colNamesAE, $slength, $key, $encryptionType);
|
||||
$insertQuery = constructInsertQuery($tableName, $dataTypes, $colNames, $colNamesAE);
|
||||
|
||||
try {
|
||||
$stmt = $conn->query("DROP TABLE IF EXISTS $tableName");
|
||||
$stmt = $conn->query($createQuery);
|
||||
} catch(Exception $error) {
|
||||
print_r($error);
|
||||
die("Creating an encrypted table failed when it shouldn't have!\n");
|
||||
}
|
||||
|
||||
insertValues($conn, $insertQuery, $dataTypes, $testValues);
|
||||
|
||||
if ($count == 0) {
|
||||
testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $thresholds, $key, $encryptionType, 'correct');
|
||||
testPatternMatch($conn, $tableName, $patterns, $dataTypes, $colNames, $key, $encryptionType, 'correct');
|
||||
}
|
||||
++$count;
|
||||
|
||||
if ($key == $targetKey and $encryptionType == $targetType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Split the data type array, because for some reason we get an error
|
||||
// if the query is too long (>2000 characters)
|
||||
// TODO: This is a known issue, follow up on it.
|
||||
$splitDataTypes = array_chunk($dataTypes, 5);
|
||||
$encryptionFailed = false;
|
||||
foreach ($splitDataTypes as $split) {
|
||||
$alterQuery = constructAlterQuery($tableName, $colNamesAE, $split, $targetKey, $targetType, $slength);
|
||||
|
||||
try {
|
||||
$stmt = $conn->query($alterQuery);
|
||||
if (!isEnclaveEnabled($key) or !isEnclaveEnabled($targetKey)) {
|
||||
die("Encrypting should have failed with key $targetKey and encryption type $encryptionType\n");
|
||||
}
|
||||
} catch (PDOException $error) {
|
||||
if (!isEnclaveEnabled($key) or !isEnclaveEnabled($targetKey)) {
|
||||
$e = $error->errorInfo;
|
||||
checkErrors($e, array('42000', '33543'));
|
||||
$encryptionFailed = true;
|
||||
continue;
|
||||
} else {
|
||||
print_r($error);
|
||||
die("Encrypting failed when it shouldn't have! key = $targetKey and type = $targetType\n");
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($encryptionFailed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $thresholds, $targetKey, $targetType, 'correct');
|
||||
testPatternMatch($conn, $tableName, $patterns, $dataTypes, $colNames, $targetKey, $targetType, 'correct');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "Done.\n";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done.
|
95
test/functional/pdo_sqlsrv/pdo_aev2_wrong_attestation.phpt
Normal file
95
test/functional/pdo_sqlsrv/pdo_aev2_wrong_attestation.phpt
Normal file
|
@ -0,0 +1,95 @@
|
|||
--TEST--
|
||||
Try re-encrypting a table with ColumnEncryption set to the wrong attestation URL, which should fail.
|
||||
--DESCRIPTION--
|
||||
This test cycles through $encryptionTypes and $keys, creating an encrypted table
|
||||
each time, then cycles through $targetTypes and $targetKeys to try re-encrypting
|
||||
the table with different combinations of enclave-enabled and non-enclave keys
|
||||
and encryption types.
|
||||
The sequence of operations is the following:
|
||||
1. Connect with correct attestation information.
|
||||
2. Create an encrypted table with two columns for each AE-supported data type, one encrypted and one not encrypted.
|
||||
3. Insert some data.
|
||||
4. Disconnect and reconnect with a faulty attestation URL.
|
||||
5. Test comparison and pattern matching by comparing the results for the encrypted and non-encrypted columns.
|
||||
Equality should work with deterministic encryption as in AE v1, but other computations should fail.
|
||||
6. Try re-encrypting the table. This should fail.
|
||||
--SKIPIF--
|
||||
<?php require("skipif_not_hgs.inc"); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
require_once("AE_v2_values.inc");
|
||||
require_once("pdo_AE_functions.inc");
|
||||
|
||||
$initialAttestation = $attestation;
|
||||
|
||||
// Create a table for each key and encryption type, re-encrypt using each
|
||||
// combination of target key and target encryption
|
||||
foreach ($keys as $key) {
|
||||
foreach ($encryptionTypes as $encryptionType) {
|
||||
|
||||
// $count is used to ensure we only run testCompare and
|
||||
// testPatternMatch once for the initial table
|
||||
$count = 0;
|
||||
foreach ($targetKeys as $targetKey) {
|
||||
foreach ($targetTypes as $targetType) {
|
||||
|
||||
$conn = connect($server, $initialAttestation);
|
||||
|
||||
// Create an encrypted table
|
||||
$createQuery = constructAECreateQuery($tableName, $dataTypes, $colNames, $colNamesAE, $slength, $key, $encryptionType);
|
||||
$insertQuery = constructInsertQuery($tableName, $dataTypes, $colNames, $colNamesAE);
|
||||
|
||||
try {
|
||||
$stmt = $conn->query("DROP TABLE IF EXISTS $tableName");
|
||||
$stmt = $conn->query($createQuery);
|
||||
} catch(Exception $error) {
|
||||
print_r($error);
|
||||
die("Creating an encrypted table failed when it shouldn't have!\n");
|
||||
}
|
||||
|
||||
insertValues($conn, $insertQuery, $dataTypes, $testValues);
|
||||
unset($conn);
|
||||
|
||||
// Reconnect with a faulty attestation URL
|
||||
$comma = strpos($attestation, ',');
|
||||
$newAttestation = substr_replace($attestation, 'x', $comma+1, 0);
|
||||
|
||||
$conn = connect($server, $newAttestation);
|
||||
|
||||
if ($count == 0) {
|
||||
testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $thresholds, $key, $encryptionType, 'wrongurl');
|
||||
testPatternMatch($conn, $tableName, $patterns, $dataTypes, $colNames, $key, $encryptionType, 'wrongurl');
|
||||
}
|
||||
++$count;
|
||||
|
||||
if ($key == $targetKey and $encryptionType == $targetType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$alterQuery = constructAlterQuery($tableName, $colNamesAE, $dataTypes, $targetKey, $targetType, $slength);
|
||||
|
||||
try {
|
||||
$stmt = $conn->query($alterQuery);
|
||||
|
||||
// Query should fail and trigger catch block before getting here
|
||||
die("Encrypting should have failed with key $targetKey and encryption type $targetType\n");
|
||||
} catch(Exception $error) {
|
||||
if (!isEnclaveEnabled($key) or !isEnclaveEnabled($targetKey)) {
|
||||
$e = $error->errorInfo;
|
||||
checkErrors($e, array('42000', '33543'));
|
||||
} else {
|
||||
$e = $error->errorInfo;
|
||||
checkErrors($e, array('CE405', '0'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "Done.\n";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done.
|
36
test/functional/pdo_sqlsrv/skipif_not_hgs.inc
Normal file
36
test/functional/pdo_sqlsrv/skipif_not_hgs.inc
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
// For AE v2, need ODBC driver 17.4 or above, an enclave enabled
|
||||
// SQL Server, and a HGS server. The HGS server and SQL Server
|
||||
// are the same for testing purposes.
|
||||
|
||||
if (!extension_loaded("sqlsrv")) {
|
||||
die("skip Extension not loaded");
|
||||
}
|
||||
|
||||
require_once("MsSetup.inc");
|
||||
|
||||
$connectionInfo = array("UID"=>$uid, "PWD"=>$pwd, "Driver" => $driver);
|
||||
|
||||
$conn = sqlsrv_connect( $server, $connectionInfo );
|
||||
if ($conn === false) {
|
||||
die( "skip Could not connect during SKIPIF." );
|
||||
}
|
||||
|
||||
$msodbcsql_ver = sqlsrv_client_info($conn)["DriverVer"];
|
||||
$msodbcsql_maj = explode(".", $msodbcsql_ver)[0];
|
||||
$msodbcsql_min = explode(".", $msodbcsql_ver)[1];
|
||||
|
||||
if ($msodbcsql_maj < 17) {
|
||||
die("skip Unsupported ODBC driver version");
|
||||
}
|
||||
|
||||
if ($msodbcsql_min < 4 and $msodbcsql_maj == 17) {
|
||||
die("skip Unsupported ODBC driver version");
|
||||
}
|
||||
|
||||
// Get SQL Server
|
||||
$server_info = sqlsrv_server_info($conn);
|
||||
if (strpos($server_info['SQLServerName'], 'PHPHGS') === false) {
|
||||
die("skip Server is not HGS enabled");
|
||||
}
|
||||
?>
|
BIN
test/functional/setup/AEV2Cert.pfx
Normal file
BIN
test/functional/setup/AEV2Cert.pfx
Normal file
Binary file not shown.
|
@ -1,35 +1,98 @@
|
|||
/* DROP Column Encryption Key first, Column Master Key cannot be dropped until no encryption depends on it */
|
||||
IF EXISTS (SELECT * FROM sys.column_encryption_keys WHERE [name] LIKE '%AEColumnKey%')
|
||||
|
||||
/* DROP Column Encryption Keys first, Column Master Keys cannot be dropped until no CEKs depend on them */
|
||||
IF EXISTS (SELECT * FROM sys.column_encryption_keys WHERE [name] LIKE '%AEColumnKey%' OR [name] LIKE '%-win-%')
|
||||
BEGIN
|
||||
DROP COLUMN ENCRYPTION KEY [AEColumnKey]
|
||||
DROP COLUMN ENCRYPTION KEY [CEK-win-enclave]
|
||||
DROP COLUMN ENCRYPTION KEY [CEK-win-enclave2]
|
||||
DROP COLUMN ENCRYPTION KEY [CEK-win-noenclave]
|
||||
DROP COLUMN ENCRYPTION KEY [CEK-win-noenclave2]
|
||||
END
|
||||
GO
|
||||
|
||||
/* Can finally drop Column Master Key after the Encryption Key is dropped */
|
||||
IF EXISTS (SELECT * FROM sys.column_master_keys WHERE [name] LIKE '%AEMasterKey%')
|
||||
|
||||
/* Can finally drop Column Master Keys after the Column Encryption Keys are dropped */
|
||||
IF EXISTS (SELECT * FROM sys.column_master_keys WHERE [name] LIKE '%AEMasterKey%' OR [name] LIKE '%-win-%')
|
||||
BEGIN
|
||||
DROP COLUMN MASTER KEY [AEMasterKey]
|
||||
DROP COLUMN MASTER KEY [CMK-win-enclave]
|
||||
DROP COLUMN MASTER KEY [CMK-win-noenclave]
|
||||
END
|
||||
GO
|
||||
|
||||
/* Recreate the Column Master Key */
|
||||
/* Create the Column Master Keys */
|
||||
/* AKVMasterKey is a non-enclave enabled key for AE v1 testing */
|
||||
/* The enclave-enabled master key requires an ENCLAVE_COMPUTATIONS clause */
|
||||
CREATE COLUMN MASTER KEY [AEMasterKey]
|
||||
WITH
|
||||
(
|
||||
KEY_STORE_PROVIDER_NAME = N'MSSQL_CERTIFICATE_STORE',
|
||||
KEY_PATH = N'CurrentUser/my/237F94738E7F5214D8588006C2269DBC6B370816'
|
||||
KEY_STORE_PROVIDER_NAME = N'MSSQL_CERTIFICATE_STORE',
|
||||
KEY_PATH = N'CurrentUser/my/237F94738E7F5214D8588006C2269DBC6B370816'
|
||||
)
|
||||
GO
|
||||
|
||||
/* Create Column Encryption Key using the Column Master Key */
|
||||
/* The enclave-enabled master key requires an ENCLAVE_COMPUTATIONS clause */
|
||||
CREATE COLUMN MASTER KEY [CMK-win-enclave]
|
||||
WITH
|
||||
(
|
||||
KEY_STORE_PROVIDER_NAME = N'MSSQL_CERTIFICATE_STORE',
|
||||
KEY_PATH = N'CurrentUser/My/D9C0572FA54B221D6591C473BAEA53FE61AAC854',
|
||||
ENCLAVE_COMPUTATIONS (SIGNATURE = 0xA1150DE565E9C132D2AAB8FF8B228EAA8DA804F250B5B422874CB608A3B274DDE523E71B655A3EFC6C3018B632701E9205BAD80C178614E1FE821C6807B0E70BCF11168FC4B202638905C5F016EDBADACA23C696B79772C56825F36EB8C0366B130C91D85362E560C9D2FDD20DCAE99619256045CA2725DEC9E0C115CAEB9EA686CCB0DE0D53D2056C01752B17B634FC6DBB51EA043F607349489722DB8A086CBC876649284A8352822DD22B328E7BA3D671CCDF54CDAAF61DFD6AF2EAAC14E03897324234AB103C45AB48131C1CD19040782359FC920A0AF61BA9842ADFB76C3196CBC6EB9C0A679926ED63E092B7C8643232C97A64C7F918104C210787A56F)
|
||||
)
|
||||
GO
|
||||
|
||||
CREATE COLUMN MASTER KEY [CMK-win-noenclave]
|
||||
WITH
|
||||
(
|
||||
KEY_STORE_PROVIDER_NAME = N'MSSQL_CERTIFICATE_STORE',
|
||||
KEY_PATH = N'CurrentUser/My/D9C0572FA54B221D6591C473BAEA53FE61AAC854'
|
||||
)
|
||||
GO
|
||||
|
||||
/* Now we can create the Column Encryption Keys */
|
||||
/* ENCRYPTED_VALUE is generated by SSMS and it is always the same if the same Certificate is imported */
|
||||
CREATE COLUMN ENCRYPTION KEY [AEColumnKey]
|
||||
WITH VALUES
|
||||
(
|
||||
COLUMN_MASTER_KEY = [AEMasterKey],
|
||||
ALGORITHM = 'RSA_OAEP',
|
||||
ENCRYPTED_VALUE = 0x016E000001630075007200720065006E00740075007300650072002F006D0079002F00320033003700660039003400370033003800650037006600350032003100340064003800350038003800300030003600630032003200360039006400620063003600620033003700300038003100360039DE2397A08F6313E7820D75382D8469BE1C8F3CD47E3240A5A6D6F82D322F6EB1B103C9C47999A69FFB164D37E7891F60FFDB04ADEADEB990BE88AE488CAFB8774442DF909D2EF8BB5961A5C11B85BA7903E0E453B27B49CE0A30D14FF4F412B5737850A4C564B44C744E690E78FAECF007F9005E3E0FB4F8D6C13B016A6393B84BB3F83FEED397C4E003FF8C5BBDDC1F6156349A8B40EDC26398C9A03920DD81B9197BC83A7378F79ECB430A04B4CFDF3878B0219BB629F5B5BF3C2359A7498AD9A6F5D63EF15E060CDB10A65E6BF059C7A32237F0D9E00C8AC632CCDD68230774477D4F2E411A0E4D9B351E8BAA87793E64456370D91D4420B5FD9A252F6D9178AE3DD02E1ED57B7F7008114272419F505CBCEB109715A6C4331DEEB73653990A7140D7F83089B445C59E4858809D139658DC8B2781CB27A749F1CE349DC43238E1FBEAE0155BF2DBFEF6AFD9FD2BD1D14CEF9AC125523FD1120488F24416679A6041184A2719B0FC32B6C393FF64D353A3FA9BC4FA23DFDD999B0771A547B561D72B92A0B2BB8B266BC25191F2A0E2F8D93648F8750308DCD79BE55A2F8D5FBE9285265BEA66173CD5F5F21C22CC933AE2147F46D22BFF329F6A712B3D19A6488DDEB6FDAA5B136B29ADB0BA6B6D1FD6FBA5D6A14F76491CB000FEE4769D5B268A3BF50EA3FBA713040944558EDE99D38A5828E07B05236A4475DA27915E
|
||||
COLUMN_MASTER_KEY = [AEMasterKey],
|
||||
ALGORITHM = 'RSA_OAEP',
|
||||
ENCRYPTED_VALUE = 0x016E000001630075007200720065006E00740075007300650072002F006D0079002F00320033003700660039003400370033003800650037006600350032003100340064003800350038003800300030003600630032003200360039006400620063003600620033003700300038003100360039DE2397A08F6313E7820D75382D8469BE1C8F3CD47E3240A5A6D6F82D322F6EB1B103C9C47999A69FFB164D37E7891F60FFDB04ADEADEB990BE88AE488CAFB8774442DF909D2EF8BB5961A5C11B85BA7903E0E453B27B49CE0A30D14FF4F412B5737850A4C564B44C744E690E78FAECF007F9005E3E0FB4F8D6C13B016A6393B84BB3F83FEED397C4E003FF8C5BBDDC1F6156349A8B40EDC26398C9A03920DD81B9197BC83A7378F79ECB430A04B4CFDF3878B0219BB629F5B5BF3C2359A7498AD9A6F5D63EF15E060CDB10A65E6BF059C7A32237F0D9E00C8AC632CCDD68230774477D4F2E411A0E4D9B351E8BAA87793E64456370D91D4420B5FD9A252F6D9178AE3DD02E1ED57B7F7008114272419F505CBCEB109715A6C4331DEEB73653990A7140D7F83089B445C59E4858809D139658DC8B2781CB27A749F1CE349DC43238E1FBEAE0155BF2DBFEF6AFD9FD2BD1D14CEF9AC125523FD1120488F24416679A6041184A2719B0FC32B6C393FF64D353A3FA9BC4FA23DFDD999B0771A547B561D72B92A0B2BB8B266BC25191F2A0E2F8D93648F8750308DCD79BE55A2F8D5FBE9285265BEA66173CD5F5F21C22CC933AE2147F46D22BFF329F6A712B3D19A6488DDEB6FDAA5B136B29ADB0BA6B6D1FD6FBA5D6A14F76491CB000FEE4769D5B268A3BF50EA3FBA713040944558EDE99D38A5828E07B05236A4475DA27915E
|
||||
)
|
||||
GO
|
||||
GO
|
||||
|
||||
/* There are two enclave enabled keys and two non-enclave enabled keys to test the case where a user
|
||||
tries to reencrypt a table from one enclave enabled key to another enclave enabled key, or from a
|
||||
non-enclave key to another non-enclave key */
|
||||
CREATE COLUMN ENCRYPTION KEY [CEK-win-enclave]
|
||||
WITH VALUES
|
||||
(
|
||||
COLUMN_MASTER_KEY = [CMK-win-enclave],
|
||||
ALGORITHM = 'RSA_OAEP',
|
||||
ENCRYPTED_VALUE = 0x016E000001630075007200720065006E00740075007300650072002F006D0079002F0064003900630030003500370032006600610035003400620032003200310064003600350039003100630034003700330062006100650061003500330066006500360031006100610063003800350034007382EDDDE3FFCE076D5715B6BBBD22EA64E665899BEFAAD5B329F218EE30BE9F789EB98717B6FD9E50AE496AC9FEED962B23442D4FD3FBFEC9C9B65F40A3BCEC7CFAC198F4CAEE8A255F67988289EF050F9F75D0287F3DF9A9FDA0C674E48DF2CB13298AAAD039930DD909EEE71682CC8A90202D3F2A1F1037BB20B1954C8B6A11F05D104CA9DAF1561C6B2F9DBB08BCE17244157B751C02FC1730E387F372C31327F2834D19AF626D0B46B152615F05FA2F3566350312CDE6DE1160B3C1D0FD35FAF13891C04711DF184DA501AA51D16BF009EA71A2D28E201804C6F8F9100E90234923B2713EA7988861FBA4E292E5518FFC02CCBD2513EDA871F6E03ECDDD309619557277C10A07906E55BA3F59A6A18834B4CD5185DA4B4574A18B8B1AC53A2C36B033D7A72443F1438E76E37306A1F92AC30BC751F6D7ED1633FEE807440E1D6096C53C5E3E33828C9C59E8761E5BAD341C6D9E2BD1F2B5C3992666620CAA38C4645C154976EF62AE80161A9F7700C96875A72995E1C585918B28F65060F1B8B96417328F6DEDFCA79ED9F01EAB19FF4E3163F9963BA26E9B58031A04320CC73702A6ED438513E0F8ABA1966B53114038CC587050F90D9CD0F9E26CA9749723ABA85CF31F963A5E85E04993B2B2869725E734BE8FCFD30A801825582730B49C00A2058C02D3312D6D8E82078FF4F77C5FF9CE6E9D140F1A4517635AB784
|
||||
)
|
||||
GO
|
||||
|
||||
CREATE COLUMN ENCRYPTION KEY [CEK-win-enclave2]
|
||||
WITH VALUES
|
||||
(
|
||||
COLUMN_MASTER_KEY = [CMK-win-enclave],
|
||||
ALGORITHM = 'RSA_OAEP',
|
||||
ENCRYPTED_VALUE = 0x016E000001630075007200720065006E00740075007300650072002F006D0079002F0064003900630030003500370032006600610035003400620032003200310064003600350039003100630034003700330062006100650061003500330066006500360031006100610063003800350034006B4D40ABF0975AF7C5CA7D1F4345DE437318556F5A2380DCFE4AB792DC3A424EABC80EA24EE850FACD94F04809C8B32674C6FF2D966FA7F9F9E522990E5F5011515BA4B7EF3603619D8A4BF46AA9B769A8A4417462C4B0303F995F04964A2E328A503D87CD1AB85ECFCB8241D0C815540989DC33E58EDCCBAFF0753E196813E3FCCC5A3C9E4277DD528AE276F1F795973A4DF8D1BB3B1F405B5F35A6A583F0BB86BAD7FCADC1FCF6B14B602890109360FAB67D6A27DE542AE87784C40FEB9071AC34C4C40C92A6C153A4A38B6DA3AD48ED39E32D6D161ACE7EFE516B414139A831D878C13FF178649823C4EFDC8E5DB4C02F2147CC76965C01C2F3624EB809FD4F5C2E291056077B1ABEFF1F5001C1F4248704C7C70CF63DA1EBC2FEC4A3DF919BA4F6B465819BC4587599C2E7499CDE62D7C335CE7BBCFC72242A8F41C1B5C94DEB0A9AF49B723759A8CD9751EE70DDEBAFA1957382287F621790543841EBCCA0007BA030CAF29E9FBF8CEB4FEC88673F47B5EC3B5F759BBDD8ED2EAF572711D78286E4294B89FF6EBFEE4968B4596AF3B5C34985F28E886F6C211F385326F10ED62602007589FC494372902FB32B0E3D67A8C64F43A87B06EE9F2CF074EB6F3EC7A431733EDA8745051B7A4AA4C020797A9492E6A3BA643D031E491497BF17539993871085AC249D0AD82203CD442F69D6C686D26F4D17BA46B69D3CB7E395
|
||||
)
|
||||
GO
|
||||
|
||||
CREATE COLUMN ENCRYPTION KEY [CEK-win-noenclave]
|
||||
WITH VALUES
|
||||
(
|
||||
COLUMN_MASTER_KEY = [CMK-win-noenclave],
|
||||
ALGORITHM = 'RSA_OAEP',
|
||||
ENCRYPTED_VALUE = 0x016E000001630075007200720065006E00740075007300650072002F006D0079002F00640039006300300035003700320066006100350034006200320032003100640036003500390031006300340037003300620061006500610035003300660065003600310061006100630038003500340042DC7A3AAAD184E01288C0913EFB6FEC6167CD8EA08A5F46ADCCC34D3AA6A1BDDDA15EA3DD219ED8795AB05C0111E48EA35A82ADDF2A206FACBBF4FD73D01C004DF627012D3950FEBCA4BBEDBDF97BA77033728D8873BA81E1C7BDCBE04BB3AA7EB42A1EDDBEF9B1CA9477ADA33F76711FEDF782CA1BD3C0104FDEB9E0D66DFCEC7D3C236906481B44F04457549658635322447742FB00B6D6F36A7CFCC56BB39F7280736BC25FD499F9CBA2F63CE11D53E536FD4A266929E06CF2BDBAF229894A77EDE140323B674ECF28C58C3E0B6C2E9407AD1A26776CB55D68B8286F64787CE5A468CFA27295D6069EFA5D65CD9A04602E861F4504F2611AAE6A8ADE33038A2BECE8BD7CF5B48567C217E324F11935C552FD25FE1FEFB152684BD1B3F8EB70EC9F6439340CE82CD8E74DD5986A6C4F9E8336ED4AC804FAD800A3EA324F78DCE37832035C3DC92782A06150916D01322A80767D1A36D7A8D9BCF6727DCE6AC67A168FA8B8B5032E60DCB178B21A860F2D98BE09DA9BA5DCCBD0D339369FF3C50C7993463372CF5B1DA9FAA12CD16E76F5961C01EADC5804C7F22227E2095BAD0F90A47B6330B1B43407E01DE5B61CEBD542A93797428AD84376E9362EADE6DDD103B9EC96E616A2ECED7D1D665B5B872E77FC024AD92AB4A8335D12D41BDD152790E87590798C1005956F9F92D4DD0C1C9852D147F7CB55B3224DE8EF593F
|
||||
)
|
||||
GO
|
||||
|
||||
CREATE COLUMN ENCRYPTION KEY [CEK-win-noenclave2]
|
||||
WITH VALUES
|
||||
(
|
||||
COLUMN_MASTER_KEY = [CMK-win-noenclave],
|
||||
ALGORITHM = 'RSA_OAEP',
|
||||
ENCRYPTED_VALUE = 0x016E000001630075007200720065006E00740075007300650072002F006D0079002F0064003900630030003500370032006600610035003400620032003200310064003600350039003100630034003700330062006100650061003500330066006500360031006100610063003800350034009014CD16FC878CEA2DE91C8C681AE86C7C062D8BD88C4CEE501A89FEAC47356D7181644A350F72B5F6023DA2B9E26C5A2522C08B1910D390068CF26794F4BA7B0298A6676B4DC6DED913E3B077B56224D2E1A3FE4EF33F58FE44CFC3DD67E54FB15BE8E29ABAF8357F378FBEDA3EBF9868A54746074D5E0E798047867E1ABD39AD0645BB8E071C72BFC37C007CBFC58F5690A5253F444E77169B2FE92FD95897A412B2078DA3804A00723D6DF824FCA527208A1DFB377B5BA16B620213F8252E10E7D7A3719A3FBB2F7A8189792B0BCF737236963C7DDCA6366F7B04F127925A1F8DDBB1B5A01D280BD300ECA3B1F31F24C8A0D517AE7BCBC3233A24E83B70A334754098DE373A1C027A4D09BB1D26C930E7501EB02464C519D19CFA0B296238AF11638C2E0688C7599E3DB1714AACF4EBFCEF63E1EE521A8E38E3BEFD4EF4991A15E8DD5CFD94E58E68754F3E90BC117025C01562F6440417A42612BE9C8871A18108CBE3E96DA7E35C45171C03E1DFBB3CA1E35A6D322F2D5B79E2BF2A07F14136DA4A768E08E2A7F1A42E04B717CB6AE3D1A3FA0EACCFC9CEC27DB53761E13DE1F55B410A65FB441D50CF8B2153B64925B1CEBDE062B5CAF4C99C41FED6836327037C46515710F16DC611305A0EBA1943A9BA5CC6889626990879713E9C95BB54D6A8A3C1C05A10AFE142B2487A1F0A07B57841E940CC9816E3F43CAE3CB7
|
||||
)
|
||||
GO
|
||||
|
|
|
@ -31,6 +31,8 @@ def setupAE(conn_options, dbname):
|
|||
# import self signed certificate
|
||||
inst_command = "certutil -user -p '' -importPFX My PHPcert.pfx NoRoot"
|
||||
executeCommmand(inst_command)
|
||||
inst_command = "certutil -user -p '' -importPFX My AEV2Cert.pfx NoRoot"
|
||||
executeCommmand(inst_command)
|
||||
# create Column Master Key and Column Encryption Key
|
||||
script_command = 'sqlcmd -I ' + conn_options + ' -i ae_keys.sql -d ' + dbname
|
||||
executeCommmand(script_command)
|
||||
|
|
163
test/functional/sqlsrv/AE_v2_values.inc
Normal file
163
test/functional/sqlsrv/AE_v2_values.inc
Normal file
|
@ -0,0 +1,163 @@
|
|||
<?php
|
||||
include("MsSetup.inc");
|
||||
|
||||
$tableName = "aev2test";
|
||||
|
||||
// Names of the encryption keys, depending on whether we are using Windows
|
||||
// or AKV authentication (defined in MsSetup.inc). -enclave keys are enclave
|
||||
// enabled, -noenclave keys are not enclave enabled.
|
||||
// $targetKeys are the keys used for re-encrypting encrypted columns
|
||||
if ($keystore == 'win') {
|
||||
$keys = array("CEK-win-enclave", "CEK-win-noenclave");
|
||||
$targetKeys = array("CEK-win-enclave", "CEK-win-enclave2", "CEK-win-noenclave", "CEK-win-noenclave2");
|
||||
} elseif ($keystore == 'akv') {
|
||||
$keys = array("CEK-akv-enclave", "CEK-akv-noenclave");
|
||||
$targetKeys = array("CEK-akv-enclave", "CEK-akv-enclave2", "CEK-akv-noenclave", "CEK-akv-noenclave2");
|
||||
}
|
||||
|
||||
// $targetTypes are the encryption types used for re-encrypting encrypted columns
|
||||
$encryptionTypes = array("Randomized", "Deterministic");
|
||||
$targetTypes = array("Deterministic", "Randomized");
|
||||
|
||||
// Length of the string-type columns. $slength is length as a string instead of integer
|
||||
$length = 64;
|
||||
$slength = '64';
|
||||
|
||||
// Testing the following data types.
|
||||
// TODO: Add binary string types and fix smalldatetime issues.
|
||||
$dataTypes = array('integer',
|
||||
'bigint',
|
||||
'smallint',
|
||||
'tinyint',
|
||||
'bit',
|
||||
'float',
|
||||
'real',
|
||||
'numeric',
|
||||
'char',
|
||||
'nchar',
|
||||
'varchar',
|
||||
'nvarchar',
|
||||
'varchar(max)',
|
||||
'nvarchar(max)',
|
||||
//'binary',
|
||||
//'varbinary',
|
||||
//'varbinary(max)',
|
||||
'date',
|
||||
'time',
|
||||
'datetime',
|
||||
'datetime2',
|
||||
'datetimeoffset',
|
||||
//'smalldatetime',
|
||||
);
|
||||
|
||||
// Construct the array of column names. Two columns for each data type,
|
||||
// one encrypted (suffixed _AE) and one not encrypted.
|
||||
$colNames = array();
|
||||
$colNamesAE = array();
|
||||
|
||||
foreach ($dataTypes as $type) {
|
||||
$column = str_replace(array("(", ",", ")"), array("_", "_", ""), $type);
|
||||
$colNames[$type] = "c_".$column;
|
||||
$colNamesAE[$type] = "c_".$column."_AE";
|
||||
}
|
||||
|
||||
// The test data below is a mixture of random data and edge cases
|
||||
$testValues = array();
|
||||
|
||||
// integers
|
||||
$testValues['integer'] = array(0,-1,1,2147483647,-2147483648,65536,-100000000,128,9);
|
||||
$testValues['bigint'] = array(9223372036854775807,-40,0,1,2147483647,-2147483648,65536,-100000000000000);
|
||||
$testValues['smallint'] = array(4,-4,-32768,-99,32767,-30000,-12,-1);
|
||||
$testValues['tinyint'] = array(2,0,255,254,99,101,100,32);
|
||||
$testValues['bit'] = array(1,1,0,0,0,0,1,0);
|
||||
|
||||
// floating point
|
||||
$testValues['float'] = array(3.14159,2.3e+12,-2.3e+12,2.23e-308,1,-1.79e+308,892.3098234234001,1.2);
|
||||
$testValues['real'] = array(3.14159,2.3e+12,-2.3e+12,1.18e-38,1,-3.4e+38,892.3098234234001,1.2);
|
||||
$testValues['numeric'] = array(-3.14159,1.003456789,45.6789,-0.000000001,987987.12345,-987987.12345,100000000000,-100000000000);
|
||||
|
||||
// dates and times
|
||||
$testValues['date'] = array('2010-01-31','0485-03-31','7825-07-23','9999-12-31','1956-02-27','2018-09-01','5401-11-02','1031-10-04');
|
||||
$testValues['time'] = array('12:40:40','08:14:54.3096','23:59:59.9999','01:00:34.0101','21:45:45.4545','00:23:45.6','17:48:00.0000','20:31:49.0001');
|
||||
$testValues['datetime2'] = array('9801-01-29 11:45:23.5092856','2384-12-31 12:40:12.5434323','1984-09-25 10:40:20.0909111','9999-12-31 23:59:59.999999',
|
||||
'1259-04-29 23:59:59.9999999','1748-09-21 17:48:54.723','3125-05-31 05:00:32.4','0001-01-01 00:00:00');
|
||||
$testValues['datetimeoffset'] = array('9801-01-29 11:45:23.5092856-12:45','0001-01-01 00:00:00-02:30','1984-09-25 10:40:20.0909111+03:00','1748-09-21 17:48:54.723-09:21',
|
||||
'4896-05-18 23:17:58.3-02:00','1657-08-04 18:14:27.4','2022-03-17 07:31:45.890342+09:30','1987-10-25 14:27:34.6320945-06:00');
|
||||
$testValues['datetime'] = array('9801-01-29 11:45:23.509','2384-12-31 12:40:12.543','1984-09-25 10:40:20.090','9999-12-31 23:59:59.997',
|
||||
'2753-04-29 23:59:59.997','1948-09-21 17:48:54.723','3125-05-31 05:00:32.4','2001-01-01 00:00:00');
|
||||
$testValues['smalldatetime'] = array('1998-06-13 04:00:30','1985-03-31 12:40:40','2025-07-23 05:00:32','1999-12-31 00:00:00',
|
||||
'1956-02-27 23:59:59','2018-09-01 14:35:30','2079-06-06 23:59:29.997','1931-10-04 19:52:21');
|
||||
|
||||
// strings, ascii and unicode
|
||||
$testValues['char'] = array('wvyxz', 'tposw', '%c@kj>5', 'fd4$_w@q^@!coe$7', 'abcd', 'ev72#x*fv=u$', '4rfg3sw', 'voi%###i<@@');
|
||||
$testValues['nchar'] = array('⽧㘎ⷅ㪋','af㋮ᶄḉㇼ៌ӗඣ','ኁ㵮ഖᅥ㪮ኸ⮊ߒᙵꇕ⯐គꉟफ़⻦ꈔꇼŞ','ꐷꬕ','㐯㩧㖃⺵㴰ڇལᧆ겴ꕕ겑וֹꔄ若㌉ᒵȅ㗉ꗅᡉ','ʭḪぅᾔᎀ㍏겶ꅫၞ㴉ᴳ㜞҂','','בּŬḛʼꃺꌖ㓵ꗛ᧽ഭწ社⾯㬄౧ຸฬ㐯ꋛ㗾');
|
||||
$testValues['varchar'] = array('gop093','*#$@@)%*$@!%','cio4*3do*$','zzz$a#l',' ','v#x%n!k&r@p$f^','6$gt?je#~','0x3dK#?');
|
||||
$testValues['nvarchar'] = array('ᾁẴ㔮㖖ୱܝ㐗㴴៸ழ᷂ᵄ葉អ㺓節','ӕᏵ൴ꔓὀ⾼','Ὡ','璉Džꖭ갪ụ⺭','Ӿϰᇬ㭡㇑ᵈᔆ⽹hᙎ՞ꦣ㧼ለͭ','Ĕ㬚㔈♠既','ꁈ ݫ','ꍆફⷌ㏽̗ૣܯ¢⽳㌭ゴᔓᅄѓⷉꘊⶮᏏᴗஈԋ≡ㄊହꂈ꓂ꑽრꖾŞ⽉걹ꩰോఫ㒧㒾㑷藍㵀ဲ更ꧥ');
|
||||
$testValues['varchar(max)'] = array('Q0H4@4E%v+ 3*Trx#>*r86-&d$VgjZ','AjEvVABur(A&Q@eG,A$3u"xAzl','z#dFd4z',
|
||||
'9Dvsg9B?7oktB@|OIqy<\K^\e|*7Y&yH31E-<.hQ:)g Jl`MQV>rdOhjG;B4wQ(WR[`l(pELt0FYu._T3+8tns!}Nq<i-Nqbu@]1<K{PS[SHSF(]G[G XPLlAUezBm^&qn^mK(&]ss6&yVxW_N_J5V*iKcgXyb+Hz:HS<9>rc1%n@|N|ik C@ 03a/ +H9mBq','SSs$Ie*{:D4;S]',' ','<\K^\e|*7Y&yH31E-<.hQ:','@Kg1Z6XTOgbt?CEJ|M^rkR_L4{1?l<e`N@');
|
||||
$testValues['nvarchar(max)'] = array('xᐕᛙᘡ','ퟅ㚶Ἢœäᑐï','ꐾɔᡧ㝚ஒŪᚔᘘښ곅սꕟքꀉᎠኇ','t9p4r5',
|
||||
'﹨ퟱꈽᕧු꧍ۡᢙⴖ㒘ᆾ겇ᅞ〱㝸㛾㕥গଜ㳸ꍍ匿ཋ㵔ﬠᄩ᧙ꖍᕿ㩴ఽᙿ','ⴠ⿃ᶺ͚ᎉ㵨㛢㌋㙤ᙘّᘷ㬡',
|
||||
'ᵄご︵ࣲꌤꏵퟰꖛᏠƢᵙꌵ㙈㜂琢㎯㪏㐵꒚㧶ᐁቴƯɋü㶌領㻡㉉걂ꈊㇷѼμꅲڧᶀƸڍ⭩㉩㛜ꆶ㕸ꁺꖁ㓫ޘ갧ᛄ㶋㘚ᐋꗡͭచ㖔፟ꐸ㱯ⵜᥰꃷᇂὥ㗍㚀ꀊጿἢઔܛ᎓Ե⅜㛵ྣᏝ༱⮢ΫÊ㕮⽹','繁Ɇʓӿꩭਸꆟꑇ㳋Ήᴝ㕨㰵ꇳ');
|
||||
|
||||
// binary
|
||||
$testValues['binary'] = array(0x3AD2BBC2, 720, 0xEED3A109F8F7745C, 0xD6C3E0E11A25F,0x4EACCEF38788F9,0xFFFFFFFFFFFFFFFF,6230974598,0x44E4A);
|
||||
$testValues['varbinary'] = array(58342,0xF3ED38AAC3CDC87759DE34B23C223CCDAB42109FBC888,0xE4300FF,0x000005ED309D3A45,0x06CADE379,804,0x00000000000000,0xD7209FFE4);
|
||||
$testValues['varbinary(max)'] = array(0xEF409CB33408, 0xD3EA762C78F,0,0xFFFFFFFFFFFFFFFFFFFFFFFFF,
|
||||
0x582D40EF3EB4E9762C5AA49D4E40C42CB4009ED3E75F890A2FD14BF495EFF5378A23BB782C4A40E1D0005DA3FE208A48C1F,
|
||||
0x38054,9230094389109,0xDDD4D88C4B80089D2E4A,0x88F8A8);
|
||||
|
||||
// The comparison operators to test
|
||||
$comparisons = array('=', '<', '>', '<=', '>=', '<>', '!<', '!>');
|
||||
|
||||
// Thresholds against which to use the comparison operators
|
||||
$thresholds = array('integer' => 0,
|
||||
'bigint' => 0,
|
||||
'smallint' => 1000,
|
||||
'tinyint' => 100,
|
||||
'bit' => 0,
|
||||
'float' => 1.2,
|
||||
'real' => -1.2,
|
||||
'numeric' => 45.6789,
|
||||
'char' => 'rstuv',
|
||||
'nchar' => '㊃ᾞਲ㨴꧶ꁚꅍ',
|
||||
'varchar' => '6$gt?je#~',
|
||||
'nvarchar' => 'ӕᏵ൴ꔓὀ⾼',
|
||||
'varchar(max)' => 'hijkl',
|
||||
'nvarchar(max)' => 'xᐕᛙᘡ',
|
||||
'binary' => 0x44E4A,
|
||||
'varbinary' => 0xE4300FF,
|
||||
'varbinary(max)' => 0xD3EA762C78F,
|
||||
'date' => '2010-01-31',
|
||||
'time' => '21:45:45.4545',
|
||||
'datetime' => '3125-05-31 05:00:32.4',
|
||||
'datetime2' => '2384-12-31 12:40:12.5434323',
|
||||
'datetimeoffset' => '1984-09-25 10:40:20.0909111+03:00',
|
||||
'smalldatetime' => '1998-06-13 04:00:30',
|
||||
);
|
||||
|
||||
// String patterns to test with LIKE
|
||||
$patterns = array('integer' => array('8', '48', '123'),
|
||||
'bigint' => array('000','7', '65536'),
|
||||
'smallint' => array('4','768','abc'),
|
||||
'tinyint' => array('9','0','25'),
|
||||
'bit' => array('0','1','100'),
|
||||
'float' => array('14159','.','E+','2.3','308'),
|
||||
'real' => array('30','.','e-','2.3','38'),
|
||||
'numeric' => array('0','0000','12345','abc','.'),
|
||||
'char' => array('w','@','x*fv=u$','e3'),
|
||||
'nchar' => array('af㋮','㐯ꋛ㗾','ꦣ㧼ለͭ','123'),
|
||||
'varchar' => array(' ','a','#','@@)'),
|
||||
'nvarchar' => array('ӕ','Ӿϰᇬ㭡','璉Džꖭ갪ụ⺭','更ꧥ','ꈔꇼŞ'),
|
||||
'varchar(max)' => array('A','|*7Y&','4z','@!@','AjE'),
|
||||
'nvarchar(max)' => array('t','㧶ᐁቴƯɋ','ᘷ㬡',' ','ꐾɔᡧ㝚'),
|
||||
'binary' => array('0x44E4A'),
|
||||
'varbinary' => array('0xE4300FF'),
|
||||
'varbinary(max)' => array('0xD3EA762C78F'),
|
||||
'date' => array('20','%','9-','04'),
|
||||
'time' => array('4545','.0','20:','12345',':'),
|
||||
'datetime' => array('997','12',':5','9999'),
|
||||
'datetime2' => array('3125-05-31 05:','.45','$f#','-29 ','0001'),
|
||||
'datetimeoffset' => array('+02','96',' ','5092856',':00'),
|
||||
'smalldatetime' => array('00','1999','abc',':','06'),
|
||||
);
|
||||
?>
|
|
@ -53,4 +53,6 @@ $AKVPassword = 'TARGET_AKV_PASSWORD'; // for use with KeyVaultPasswo
|
|||
$AKVClientID = 'TARGET_AKV_CLIENT_ID'; // for use with KeyVaultClientSecret
|
||||
$AKVSecret = 'TARGET_AKV_CLIENT_SECRET'; // for use with KeyVaultClientSecret
|
||||
|
||||
// for enclave computations
|
||||
$attestation = 'TARGET_ATTESTATION';
|
||||
?>
|
||||
|
|
36
test/functional/sqlsrv/skipif_not_hgs.inc
Normal file
36
test/functional/sqlsrv/skipif_not_hgs.inc
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
// For AE v2, need ODBC driver 17.4 or above, an enclave enabled
|
||||
// SQL Server, and a HGS server. The HGS server and SQL Server
|
||||
// are the same for testing purposes.
|
||||
|
||||
if (!extension_loaded("sqlsrv")) {
|
||||
die("skip Extension not loaded");
|
||||
}
|
||||
|
||||
require_once("MsSetup.inc");
|
||||
|
||||
$connectionInfo = array("UID"=>$userName, "PWD"=>$userPassword, "Driver" => $driver);
|
||||
|
||||
$conn = sqlsrv_connect( $server, $connectionInfo );
|
||||
if ($conn === false) {
|
||||
die( "skip Could not connect during SKIPIF." );
|
||||
}
|
||||
|
||||
$msodbcsql_ver = sqlsrv_client_info($conn)["DriverVer"];
|
||||
$msodbcsql_maj = explode(".", $msodbcsql_ver)[0];
|
||||
$msodbcsql_min = explode(".", $msodbcsql_ver)[1];
|
||||
|
||||
if ($msodbcsql_maj < 17) {
|
||||
die("skip Unsupported ODBC driver version");
|
||||
}
|
||||
|
||||
if ($msodbcsql_min < 4 and $msodbcsql_maj == 17) {
|
||||
die("skip Unsupported ODBC driver version");
|
||||
}
|
||||
|
||||
// Get SQL Server
|
||||
$server_info = sqlsrv_server_info($conn);
|
||||
if (strpos($server_info['SQLServerName'], 'PHPHGS') === false) {
|
||||
die("skip Server is not HGS enabled");
|
||||
}
|
||||
?>
|
518
test/functional/sqlsrv/sqlsrv_AE_functions.inc
Normal file
518
test/functional/sqlsrv/sqlsrv_AE_functions.inc
Normal file
|
@ -0,0 +1,518 @@
|
|||
<?php
|
||||
|
||||
// Connect and clear the procedure cache
|
||||
function connect($server, $attestation_info)
|
||||
{
|
||||
include("MsSetup.inc");
|
||||
$options = array('database'=>$database,
|
||||
'uid'=>$userName,
|
||||
'pwd'=>$userPassword,
|
||||
'CharacterSet'=>'UTF-8',
|
||||
'ColumnEncryption'=>$attestation_info,
|
||||
);
|
||||
|
||||
if ($keystore == 'akv') {
|
||||
if ($AKVKeyStoreAuthentication == 'KeyVaultPassword') {
|
||||
$security_info = array('KeyStoreAuthentication'=>$AKVKeyStoreAuthentication,
|
||||
'KeyStorePrincipalId'=>$AKVPrincipalName,
|
||||
'KeyStoreSecret'=>$AKVPassword,
|
||||
);
|
||||
} elseif ($AKVKeyStoreAuthentication == 'KeyVaultClientSecret') {
|
||||
$security_info = array('KeyStoreAuthentication'=>$AKVKeyStoreAuthentication,
|
||||
'KeyStorePrincipalId'=>$AKVClientID,
|
||||
'KeyStoreSecret'=>$AKVSecret,
|
||||
);
|
||||
} else {
|
||||
die("Incorrect value for KeyStoreAuthentication keyword!\n");
|
||||
}
|
||||
|
||||
$options = array_merge($options, $security_info);
|
||||
}
|
||||
|
||||
$conn = sqlsrv_connect($server, $options);
|
||||
if (!$conn) {
|
||||
echo "Connection failed\n";
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
|
||||
// Check that enclave computations are enabled
|
||||
// See https://docs.microsoft.com/en-us/sql/relational-databases/security/encryption/configure-always-encrypted-enclaves?view=sqlallproducts-allversions#configure-a-secure-enclave
|
||||
$query = "SELECT [name], [value], [value_in_use] FROM sys.configurations WHERE [name] = 'column encryption enclave type';";
|
||||
$stmt = sqlsrv_query($conn, $query);
|
||||
$info = sqlsrv_fetch_array($stmt);
|
||||
if ($info['value'] != 1 or $info['value_in_use'] != 1) {
|
||||
die("Error: enclave computations are not enabled on the server!");
|
||||
}
|
||||
|
||||
// Enable rich computations
|
||||
sqlsrv_query($conn, "DBCC traceon(127,-1);");
|
||||
|
||||
// Free the encryption cache to avoid spurious 'operand type clash' errors
|
||||
sqlsrv_query($conn, "DBCC FREEPROCCACHE");
|
||||
|
||||
return $conn;
|
||||
}
|
||||
|
||||
// This CREATE TABLE query simply creates a non-encrypted table with
|
||||
// two columns for each data type side by side
|
||||
// This produces a query that looks like
|
||||
// CREATE TABLE aev2test2 (
|
||||
// c_integer integer,
|
||||
// c_integer_AE integer
|
||||
// )
|
||||
function constructCreateQuery($tableName, $dataTypes, $colNames, $colNamesAE, $slength)
|
||||
{
|
||||
$query = "CREATE TABLE ".$tableName." (\n ";
|
||||
|
||||
foreach ($dataTypes as $type) {
|
||||
if (dataTypeIsString($type)) {
|
||||
$query = $query.$colNames[$type]." ".$type."(".$slength."), \n ";
|
||||
$query = $query.$colNamesAE[$type]." ".$type."(".$slength."), \n ";
|
||||
} else {
|
||||
$query = $query.$colNames[$type]." ".$type.", \n ";
|
||||
$query = $query.$colNamesAE[$type]." ".$type.", \n ";
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the ", \n " from the end of the query or the comma will cause a syntax error
|
||||
$query = substr($query, 0, -7)."\n)";
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
// The ALTER TABLE query encrypts columns. Each ALTER COLUMN directive must
|
||||
// be preceded by ALTER TABLE
|
||||
// This produces a query that looks like
|
||||
// ALTER TABLE [dbo].[aev2test2]
|
||||
// ALTER COLUMN [c_integer_AE] integer
|
||||
// ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [CEK-win-enclave], ENCRYPTION_TYPE = Randomized, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NOT NULL
|
||||
// WITH
|
||||
// (ONLINE = ON); ALTER TABLE [dbo].[aev2test2]
|
||||
// ALTER COLUMN [c_bigint_AE] bigint
|
||||
// ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [CEK-win-enclave], ENCRYPTION_TYPE = Randomized, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NOT NULL
|
||||
// WITH
|
||||
// (ONLINE = ON); ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE;
|
||||
function constructAlterQuery($tableName, $colNames, $dataTypes, $key, $encryptionType, $slength)
|
||||
{
|
||||
$query = '';
|
||||
|
||||
foreach ($dataTypes as $dataType) {
|
||||
$plength = dataTypeIsString($dataType) ? "(".$slength.")" : "";
|
||||
$collate = dataTypeNeedsCollate($dataType) ? " COLLATE Latin1_General_BIN2" : "";
|
||||
$query = $query." ALTER TABLE [dbo].[".$tableName."]
|
||||
ALTER COLUMN [".$colNames[$dataType]."] ".$dataType.$plength." ".$collate."
|
||||
ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [".$key."], ENCRYPTION_TYPE = ".$encryptionType.", ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NOT NULL
|
||||
WITH
|
||||
(ONLINE = ON);";
|
||||
}
|
||||
|
||||
$query = $query." ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE;";
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
// This CREATE TABLE query creates a table with two columns for
|
||||
// each data type side by side, one plaintext and one encrypted
|
||||
// This produces a query that looks like
|
||||
// CREATE TABLE aev2test2 (
|
||||
// c_integer integer NULL,
|
||||
// c_integer_AE integer
|
||||
// COLLATE Latin1_General_BIN2 ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [CEK-win-enclave], ENCRYPTION_TYPE = Randomized, ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL
|
||||
// )
|
||||
function constructAECreateQuery($tableName, $dataTypes, $colNames, $colNamesAE, $slength, $key, $encryptionType)
|
||||
{
|
||||
$query = "CREATE TABLE ".$tableName." (\n ";
|
||||
|
||||
foreach ($dataTypes as $type) {
|
||||
$collate = dataTypeNeedsCollate($type) ? " COLLATE Latin1_General_BIN2" : "";
|
||||
|
||||
if (dataTypeIsString($type)) {
|
||||
$query = $query.$colNames[$type]." ".$type."(".$slength.") NULL, \n ";
|
||||
$query = $query.$colNamesAE[$type]." ".$type."(".$slength.") \n ";
|
||||
$query = $query." ".$collate." ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [".$key."], ENCRYPTION_TYPE = ".$encryptionType.", ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL,\n ";
|
||||
} else {
|
||||
$query = $query.$colNames[$type]." ".$type." NULL, \n ";
|
||||
$query = $query.$colNamesAE[$type]." ".$type." \n ";
|
||||
$query = $query." ".$collate." ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [".$key."], ENCRYPTION_TYPE = ".$encryptionType.", ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NULL,\n ";
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the ",\n " from the end of the query or the comma will cause a syntax error
|
||||
$query = substr($query, 0, -6)."\n)";
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
// The INSERT query for the table
|
||||
function constructInsertQuery($tableName, &$dataTypes, &$colNames, &$colNamesAE)
|
||||
{
|
||||
$queryTypes = "(";
|
||||
$valuesString = "VALUES (";
|
||||
|
||||
foreach ($dataTypes as $type) {
|
||||
$colName1 = $colNames[$type].", ";
|
||||
$colName2 = $colNamesAE[$type].", ";
|
||||
$queryTypes .= $colName1;
|
||||
$queryTypes .= $colName2;
|
||||
$valuesString .= "?, ?, ";
|
||||
}
|
||||
|
||||
// Remove the ", " from the end of the query or the comma will cause a syntax error
|
||||
$queryTypes = substr($queryTypes, 0, -2).")";
|
||||
$valuesString = substr($valuesString, 0, -2).")";
|
||||
|
||||
$insertQuery = "INSERT INTO $tableName ".$queryTypes." ".$valuesString;
|
||||
|
||||
return $insertQuery;
|
||||
}
|
||||
|
||||
function insertValues($conn, $insertQuery, $dataTypes, $testValues)
|
||||
{
|
||||
for ($v = 0; $v < sizeof($testValues['bigint']); ++$v) {
|
||||
$insertValues = array();
|
||||
|
||||
// two copies of each value for the two columns for each data type
|
||||
foreach ($dataTypes as $type) {
|
||||
$insertValues[] = $testValues[$type][$v];
|
||||
$insertValues[] = $testValues[$type][$v];
|
||||
}
|
||||
|
||||
// Insert the data using sqlsrv_prepare()
|
||||
$stmt = sqlsrv_prepare($conn, $insertQuery, $insertValues);
|
||||
if ($stmt == false) {
|
||||
print_r(sqlsrv_errors());
|
||||
die("Inserting values in encrypted table failed at prepare\n");
|
||||
}
|
||||
|
||||
if (sqlsrv_execute($stmt) == false) {
|
||||
print_r(sqlsrv_errors());
|
||||
die("Inserting values in encrypted table failed at execute\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// compareResults checks that the results between the encrypted and non-encrypted
|
||||
// columns are identical if statement execution succeeds. If statement execution
|
||||
// fails, this function checks for the correct error.
|
||||
// Arguments:
|
||||
// statement $AEstmt: Prepared statement fetching encrypted data
|
||||
// statement $nonAEstmt: Prepared statement fetching non-encrypted data
|
||||
// string $key: Name of the encryption key
|
||||
// string $encryptionType: Type of encryption, randomized or deterministic
|
||||
// string $attestation: Type of attestation - 'correct', 'enabled', or 'wrongurl'
|
||||
// string $comparison: Comparison operator
|
||||
// string $type: Data type the comparison is operating on
|
||||
function compareResults($AEstmt, $nonAEstmt, $key, $encryptionType, $attestation, $comparison='', $type='')
|
||||
{
|
||||
if (!sqlsrv_execute($nonAEstmt)) {
|
||||
print_r(sqlsrv_errors());
|
||||
die("Executing non-AE statement failed!\n");
|
||||
}
|
||||
|
||||
if(!sqlsrv_execute($AEstmt)) {
|
||||
if ($attestation == 'enabled') {
|
||||
if ($encryptionType == 'Deterministic') {
|
||||
if ($comparison == '=') {
|
||||
print_r(sqlsrv_errors());
|
||||
die("Equality comparison failed for deterministic encryption!\n");
|
||||
} else {
|
||||
$e = sqlsrv_errors();
|
||||
checkErrors($e, array('42000', '33277'));
|
||||
}
|
||||
} elseif (isEnclaveEnabled($key)) {
|
||||
$e = sqlsrv_errors();
|
||||
checkErrors($e, array('42000', '33546'));
|
||||
} elseif (!isEnclaveEnabled($key)) {
|
||||
$e = sqlsrv_errors();
|
||||
checkErrors($e, array('42000', '33277'));
|
||||
}
|
||||
} elseif ($attestation == 'wrongurl') {
|
||||
if ($encryptionType == 'Deterministic') {
|
||||
if ($comparison == '=') {
|
||||
print_r(sqlsrv_errors());
|
||||
die("Equality comparison failed for deterministic encryption!\n");
|
||||
} else {
|
||||
$e = sqlsrv_errors();
|
||||
checkErrors($e, array('42000', '33277'));
|
||||
}
|
||||
} elseif (isEnclaveEnabled($key)) {
|
||||
$e = sqlsrv_errors();
|
||||
checkErrors($e, array('CE405', '0'));
|
||||
} elseif (!isEnclaveEnabled($key)) {
|
||||
$e = sqlsrv_errors();
|
||||
checkErrors($e, array('42000', '33277'));
|
||||
}
|
||||
} elseif ($attestation == 'correct') {
|
||||
if (!isEnclaveEnabled($key) and $encryptionType == 'Randomized') {
|
||||
$e = sqlsrv_errors();
|
||||
checkErrors($e, array('42000', '33277'));
|
||||
} elseif ($encryptionType == 'Deterministic') {
|
||||
if ($comparison == '=') {
|
||||
print_r(sqlsrv_errors());
|
||||
die("Equality comparison failed for deterministic encryption!\n");
|
||||
} else {
|
||||
$e = sqlsrv_errors();
|
||||
checkErrors($e, array('42000', '33277'));
|
||||
}
|
||||
} else {
|
||||
print_r(sqlsrv_errors());
|
||||
die("Comparison failed for correct attestation when it shouldn't have!\n");
|
||||
}
|
||||
} else {
|
||||
print_r(sqlsrv_errors());
|
||||
die("Unexpected error occurred in compareResults!\n");
|
||||
}
|
||||
} else {
|
||||
// char and nchar may not return the same results - at this point
|
||||
// we've verified that statement execution works so just return
|
||||
// TODO: Check if this bug is fixed and if so, remove this if block
|
||||
if ($type == 'char' or $type == 'nchar') {
|
||||
return;
|
||||
}
|
||||
|
||||
while($AEres = sqlsrv_fetch_array($AEstmt, SQLSRV_FETCH_NUMERIC)) {
|
||||
$nonAEres = sqlsrv_fetch_array($nonAEstmt, SQLSRV_FETCH_NUMERIC);
|
||||
if (!$nonAEres) {
|
||||
print_r($AEres);
|
||||
print_r(sqlsrv_errors());
|
||||
print_r("Too many AE results for operation $comparison and data type $type!\n");
|
||||
} else {
|
||||
$i = 0;
|
||||
foreach ($AEres as $AEr) {
|
||||
if ($AEr != $nonAEres[$i]) {
|
||||
print_r("AE and non-AE results are different for operation $comparison and data type $type! For field $i, got AE result ".$AEres[$i]." and non-AE result ".$nonAEres[$i]."\n");
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
++$i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($rr = sqlsrv_fetch_array($nonAEstmt)) {
|
||||
print_r($rr);
|
||||
print_r(sqlsrv_errors());
|
||||
print_r("Too many non-AE results for operation $comparison and data type $type!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// testCompare selects based on a comparison in the WHERE clause and compares
|
||||
// the results between encrypted and non-encrypted columns, checking that the
|
||||
// results are identical
|
||||
// Arguments:
|
||||
// resource $conn: The connection
|
||||
// string $tableName: Thable name
|
||||
// array $comparisons: Comparison operations from AE_v2_values.inc
|
||||
// array $dataTypes: Data types from AE_v2_values.inc
|
||||
// array $colNames: Column names
|
||||
// array $thresholds: Values to use comparison operators against, from AE_v2_values.inc
|
||||
// string $key: Name of the encryption key
|
||||
// integer $length: Length of the string types, from AE_v2_values.inc
|
||||
// string $encryptionType: Type of encryption, randomized or deterministic
|
||||
// string $attestation: Type of attestation - 'correct', 'enabled', or 'wrongurl'
|
||||
function testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $thresholds, $length, $key, $encryptionType, $attestation)
|
||||
{
|
||||
foreach ($comparisons as $comparison) {
|
||||
foreach ($dataTypes as $type) {
|
||||
|
||||
// Unicode operations with AE require the PHPTYPE to be specified to
|
||||
// UTF-8 and the Latin1_General_BIN2 collation. If the COLLATE
|
||||
// clause is left out, we get different results between the
|
||||
// encrypted and non-encrypted columns (probably because the
|
||||
// collation was only changed in the encryption query).
|
||||
$string = dataTypeIsStringMax($type);
|
||||
$unicode = dataTypeIsUnicode($type);
|
||||
$collate = $string ? " COLLATE Latin1_General_BIN2" : "";
|
||||
$phptype = $unicode ? SQLSRV_PHPTYPE_STRING('UTF-8') : null;
|
||||
|
||||
$param = array(array($thresholds[$type], SQLSRV_PARAM_IN, $phptype, getSQLType($type, $length)));
|
||||
$AEQuery = "SELECT ".$colNames[$type]."_AE FROM $tableName WHERE ".$colNames[$type]."_AE ".$comparison." ?".$collate;
|
||||
$nonAEQuery = "SELECT ".$colNames[$type]." FROM $tableName WHERE ".$colNames[$type]." ".$comparison." ?".$collate;
|
||||
|
||||
$AEstmt = sqlsrv_prepare($conn, $AEQuery, $param);
|
||||
if (!$AEstmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
die("Preparing AE statement for comparison failed! Comparison $comparison, type $type\n");
|
||||
}
|
||||
|
||||
$nonAEstmt = sqlsrv_prepare($conn, $nonAEQuery, $param);
|
||||
if (!$nonAEstmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
die("Preparing non-AE statement for comparison failed! Comparison $comparison, type $type\n");
|
||||
}
|
||||
|
||||
compareResults($AEstmt, $nonAEstmt, $key, $encryptionType, $attestation, $comparison, $type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// testPatternMatch selects based on a pattern in the WHERE clause and compares
|
||||
// the results between encrypted and non-encrypted columns, checking that the
|
||||
// results are identical
|
||||
function testPatternMatch($conn, $tableName, $patterns, $dataTypes, $colNames, $key, $encryptionType, $attestation)
|
||||
{
|
||||
// TODO: Pattern matching doesn't work in AE for non-string types
|
||||
// without an explicit cast
|
||||
foreach ($dataTypes as $type) {
|
||||
if (!dataTypeIsStringMax($type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($patterns[$type] as $pattern) {
|
||||
$patternarray = array($pattern,
|
||||
$pattern."%",
|
||||
"%".$pattern,
|
||||
"%".$pattern."%",
|
||||
);
|
||||
|
||||
foreach ($patternarray as $spattern) {
|
||||
|
||||
// Unicode operations with AE require the PHPTYPE to be specified as
|
||||
// UTF-8 and the Latin1_General_BIN2 collation. If the COLLATE
|
||||
// clause is left out, we get different results between the
|
||||
// encrypted and non-encrypted columns (probably because the
|
||||
// collation was only changed in the encryption query).
|
||||
// We must pass the length of the pattern matching string
|
||||
// to the SQLTYPE instead of the field size, as we usually would,
|
||||
// because otherwise we would get an empty result set.
|
||||
// We need iconv_strlen to return the number of characters
|
||||
// for unicode strings, since strlen returns the number of bytes.
|
||||
$unicode = dataTypeIsUnicode($type);
|
||||
$slength = $unicode ? iconv_strlen($spattern) : strlen($spattern);
|
||||
$collate = $unicode ? " COLLATE Latin1_General_BIN2" : "";
|
||||
$phptype = $unicode ? SQLSRV_PHPTYPE_STRING('UTF-8') : null;
|
||||
$sqltype = $unicode ? SQLSRV_SQLTYPE_NCHAR($slength) : SQLSRV_SQLTYPE_CHAR($slength);
|
||||
|
||||
$param = array(array($spattern, SQLSRV_PARAM_IN, $phptype, $sqltype));
|
||||
$AEQuery = "SELECT ".$colNames[$type]."_AE FROM $tableName WHERE ".$colNames[$type]."_AE LIKE ?".$collate;
|
||||
$nonAEQuery = "SELECT ".$colNames[$type]." FROM $tableName WHERE ".$colNames[$type]." LIKE ?".$collate;
|
||||
|
||||
$AEstmt = sqlsrv_prepare($conn, $AEQuery, $param);
|
||||
if (!$AEstmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
die("Preparing AE statement for comparison failed! Comparison $comparison, type $type\n");
|
||||
}
|
||||
|
||||
$nonAEstmt = sqlsrv_prepare($conn, $nonAEQuery, $param);
|
||||
if (!$nonAEstmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
die("Preparing non-AE statement for comparison failed! Comparison $comparison, type $type\n");
|
||||
}
|
||||
|
||||
compareResults($AEstmt, $nonAEstmt, $key, $encryptionType, $attestation, $pattern, $type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the expected errors ($codes) is found in the output of sqlsrv_errors() ($errors)
|
||||
function checkErrors($errors, ...$codes)
|
||||
{
|
||||
$codeFound = false;
|
||||
|
||||
foreach ($codes as $code) {
|
||||
if ($code[0]==$errors[0][0] and $code[1]==$errors[0][1]) {
|
||||
$codeFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($codeFound == false) {
|
||||
echo "Error: ";
|
||||
print_r($errors);
|
||||
echo "\nExpected: ";
|
||||
print_r($codes);
|
||||
echo "\n";
|
||||
die("Error code not found.\n");
|
||||
}
|
||||
}
|
||||
|
||||
function isEnclaveEnabled($key)
|
||||
{
|
||||
return (strpos($key, '-enclave') !== false);
|
||||
}
|
||||
|
||||
function dataTypeIsString($dataType)
|
||||
{
|
||||
return (in_array($dataType, ["binary", "varbinary", "char", "nchar", "varchar", "nvarchar"]));
|
||||
}
|
||||
|
||||
function dataTypeIsStringMax($dataType)
|
||||
{
|
||||
return (in_array($dataType, ["binary", "varbinary", "char", "nchar", "varchar", "nvarchar", "varchar(max)", "nvarchar(max)"]));
|
||||
}
|
||||
|
||||
function dataTypeNeedsCollate($dataType)
|
||||
{
|
||||
return (in_array($dataType, ["char", "nchar", "varchar", "nvarchar", "varchar(max)", "nvarchar(max)"]));
|
||||
}
|
||||
|
||||
function dataTypeIsUnicode($dataType)
|
||||
{
|
||||
return (in_array($dataType, ["nchar", "nvarchar", "nvarchar(max)"]));
|
||||
}
|
||||
|
||||
function getSQLType($type, $length)
|
||||
{
|
||||
switch($type)
|
||||
{
|
||||
case "bigint":
|
||||
return SQLSRV_SQLTYPE_BIGINT;
|
||||
case "integer":
|
||||
return SQLSRV_SQLTYPE_INT;
|
||||
case "smallint":
|
||||
return SQLSRV_SQLTYPE_SMALLINT;
|
||||
case "tinyint":
|
||||
return SQLSRV_SQLTYPE_TINYINT;
|
||||
case "bit":
|
||||
return SQLSRV_SQLTYPE_BIT;
|
||||
case "real":
|
||||
return SQLSRV_SQLTYPE_REAL;
|
||||
case "float":
|
||||
case "double":
|
||||
return SQLSRV_SQLTYPE_FLOAT;
|
||||
case "numeric":
|
||||
return SQLSRV_SQLTYPE_NUMERIC(18,0);
|
||||
case "time":
|
||||
return SQLSRV_SQLTYPE_TIME;
|
||||
case "date":
|
||||
return SQLSRV_SQLTYPE_DATE;
|
||||
case "datetime":
|
||||
return SQLSRV_SQLTYPE_DATETIME;
|
||||
case "datetime2":
|
||||
return SQLSRV_SQLTYPE_DATETIME2;
|
||||
case "datetimeoffset":
|
||||
return SQLSRV_SQLTYPE_DATETIMEOFFSET;
|
||||
case "smalldatetime":
|
||||
return SQLSRV_SQLTYPE_SMALLDATETIME;
|
||||
case "money":
|
||||
return SQLSRV_SQLTYPE_MONEY;
|
||||
case "smallmoney":
|
||||
return SQLSRV_SQLTYPE_SMALLMONEY;
|
||||
case "xml":
|
||||
return SQLSRV_SQLTYPE_XML;
|
||||
case "uniqueidentifier":
|
||||
return SQLSRV_SQLTYPE_UNIQUEIDENTIFIER;
|
||||
case "char":
|
||||
return SQLSRV_SQLTYPE_CHAR($length);
|
||||
case "varchar":
|
||||
return SQLSRV_SQLTYPE_VARCHAR($length);
|
||||
case "varchar(max)":
|
||||
return SQLSRV_SQLTYPE_VARCHAR('max');
|
||||
case "nchar":
|
||||
return SQLSRV_SQLTYPE_NCHAR($length);
|
||||
case "nvarchar":
|
||||
return SQLSRV_SQLTYPE_NVARCHAR($length);
|
||||
case "nvarchar(max)":
|
||||
return SQLSRV_SQLTYPE_NVARCHAR('max');
|
||||
case "binary":
|
||||
case "varbinary":
|
||||
case "varbinary(max)":
|
||||
// Using a binary type here produces a 'Restricted data type attribute violation'
|
||||
return SQLSRV_SQLTYPE_BIGINT;
|
||||
default:
|
||||
die("Case is missing for $type type in getSQLType.\n");
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -15,7 +15,6 @@ function formulateSetupQuery($tableName, &$dataTypes, &$columns, &$insertQuery)
|
|||
{
|
||||
$columns = array();
|
||||
$queryTypes = "(";
|
||||
$queryTypesAE = "(";
|
||||
$valuesString = "VALUES (";
|
||||
$numTypes = sizeof($dataTypes);
|
||||
|
||||
|
|
113
test/functional/sqlsrv/sqlsrv_aev2_ce_enabled.phpt
Normal file
113
test/functional/sqlsrv/sqlsrv_aev2_ce_enabled.phpt
Normal file
|
@ -0,0 +1,113 @@
|
|||
--TEST--
|
||||
Try re-encrypting a table with ColumnEncryption set to 'enabled', which should fail.
|
||||
--DESCRIPTION--
|
||||
This test cycles through $encryptionTypes and $keys, creating an encrypted table
|
||||
each time, then cycles through $targetTypes and $targetKeys to try re-encrypting
|
||||
the table with different combinations of enclave-enabled and non-enclave keys
|
||||
and encryption types.
|
||||
The sequence of operations is the following:
|
||||
1. Connect with correct attestation information.
|
||||
2. Create an encrypted table with two columns for each AE-supported data type, one encrypted and one not encrypted.
|
||||
3. Insert some data.
|
||||
4. Disconnect and reconnect with ColumnEncryption set to 'enabled'.
|
||||
5. Test comparison and pattern matching by comparing the results for the encrypted and non-encrypted columns.
|
||||
Equality should work with deterministic encryption as in AE v1, but other computations should fail.
|
||||
6. Try re-encrypting the table. This should fail.
|
||||
--SKIPIF--
|
||||
<?php require("skipif_not_hgs.inc"); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
require_once("AE_v2_values.inc");
|
||||
require_once("sqlsrv_AE_functions.inc");
|
||||
|
||||
$initialAttestation = $attestation;
|
||||
|
||||
// Create a table for each key and encryption type, re-encrypt using each
|
||||
// combination of target key and target encryption
|
||||
foreach ($keys as $key) {
|
||||
foreach ($encryptionTypes as $encryptionType) {
|
||||
|
||||
// $count is used to ensure we only run testCompare and
|
||||
// testPatternMatch once for the initial table
|
||||
$count = 0;
|
||||
foreach ($targetKeys as $targetKey) {
|
||||
foreach ($targetTypes as $targetType) {
|
||||
|
||||
$conn = connect($server, $initialAttestation);
|
||||
|
||||
// Create an encrypted table
|
||||
$createQuery = constructAECreateQuery($tableName, $dataTypes, $colNames, $colNamesAE, $slength, $key, $encryptionType);
|
||||
$insertQuery = constructInsertQuery($tableName, $dataTypes, $colNames, $colNamesAE);
|
||||
|
||||
$stmt = sqlsrv_query($conn, "DROP TABLE IF EXISTS $tableName");
|
||||
$stmt = sqlsrv_query($conn, $createQuery);
|
||||
if(!$stmt)
|
||||
{
|
||||
print_r(editErrors(sqlsrv_errors()));
|
||||
die("Table creation failed!");
|
||||
}
|
||||
|
||||
insertValues($conn, $insertQuery, $dataTypes, $testValues);
|
||||
unset($conn);
|
||||
|
||||
// Reconnect with ColumnEncryption set to 'enabled'
|
||||
$newAttestation = 'enabled';
|
||||
|
||||
$conn = connect($server, $newAttestation);
|
||||
|
||||
if ($count == 0) {
|
||||
testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $thresholds, $length, $key, $encryptionType, 'enabled');
|
||||
testPatternMatch($conn, $tableName, $patterns, $dataTypes, $colNames, $key, $encryptionType, 'enabled');
|
||||
}
|
||||
++$count;
|
||||
|
||||
if ($key == $targetKey and $encryptionType == $targetType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$alterQuery = constructAlterQuery($tableName, $colNamesAE, $dataTypes, $targetKey, $targetType, $slength);
|
||||
|
||||
// Split the data type array, because for some reason we get an error
|
||||
// if the query is too long (>2000 characters)
|
||||
$splitDataTypes = array_chunk($dataTypes, 5);
|
||||
$encryptionFailed = false;
|
||||
|
||||
foreach ($splitDataTypes as $split) {
|
||||
|
||||
$alterQuery = constructAlterQuery($tableName, $colNamesAE, $split, $targetKey, $targetType, $slength);
|
||||
$stmt = sqlsrv_query($conn, $alterQuery);
|
||||
|
||||
if(!$stmt) {
|
||||
if (!isEnclaveEnabled($key) or !isEnclaveEnabled($targetKey)) {
|
||||
|
||||
$e = sqlsrv_errors();
|
||||
checkErrors($e, array('42000', '33543'));
|
||||
$encryptionFailed = true;
|
||||
continue;
|
||||
} else {
|
||||
$e = sqlsrv_errors();
|
||||
checkErrors($e, array('42000', '33546'));
|
||||
$encryptionFailed = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
continue;
|
||||
} else {
|
||||
die("Encrypting should have failed with key $targetKey and encryption type $encryptionType!\n");
|
||||
}
|
||||
}
|
||||
|
||||
if ($encryptionFailed) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "Done.\n";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done.
|
138
test/functional/sqlsrv/sqlsrv_aev2_encrypt_plaintext.phpt
Normal file
138
test/functional/sqlsrv/sqlsrv_aev2_encrypt_plaintext.phpt
Normal file
|
@ -0,0 +1,138 @@
|
|||
--TEST--
|
||||
Test rich computations and in-place encryption of plaintext with AE v2.
|
||||
--DESCRIPTION--
|
||||
This test cycles through $encryptionTypes and $keys, creating a plaintext table
|
||||
each time, then trying to encrypt it with different combinations of enclave-enabled and non-enclave keys
|
||||
and encryption types. It then cycles through $targetTypes and $targetKeys to try re-encrypting
|
||||
the table with different target combinations of enclave-enabled and non-enclave keys
|
||||
and encryption types.
|
||||
The sequence of operations is the following:
|
||||
1. Create a table in plaintext with two columns for each AE-supported data type.
|
||||
2. Insert some data in plaintext.
|
||||
3. Encrypt one column for each data type.
|
||||
4. Perform rich computations on each AE-enabled column (comparisons and pattern matching) and compare the result
|
||||
to the same query on the corresponding non-AE column for each data type.
|
||||
5. Ensure the two results are the same.
|
||||
6. Re-encrypt the table using new key and/or encryption type.
|
||||
7. Compare computations as in 4. above.
|
||||
--SKIPIF--
|
||||
<?php require("skipif_not_hgs.inc"); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
require_once("AE_v2_values.inc");
|
||||
require_once("sqlsrv_AE_functions.inc");
|
||||
|
||||
$initialAttestation = $attestation;
|
||||
|
||||
// Create a table for each key and encryption type, re-encrypt using each
|
||||
// combination of target key and target encryption
|
||||
foreach ($keys as $key) {
|
||||
foreach ($encryptionTypes as $encryptionType) {
|
||||
|
||||
// $count is used to ensure we only run testCompare and
|
||||
// testPatternMatch once for the initial table
|
||||
$count = 0;
|
||||
$conn = connect($server, $attestation);
|
||||
|
||||
foreach ($targetKeys as $targetKey) {
|
||||
foreach ($targetTypes as $targetType) {
|
||||
|
||||
// Free the encryption cache to avoid spurious 'operand type clash' errors
|
||||
sqlsrv_query($conn, "DBCC FREEPROCCACHE");
|
||||
|
||||
// Create and populate a non-encrypted table
|
||||
$createQuery = constructCreateQuery($tableName, $dataTypes, $colNames, $colNamesAE, $slength);
|
||||
$insertQuery = constructInsertQuery($tableName, $dataTypes, $colNames, $colNamesAE);
|
||||
|
||||
$stmt = sqlsrv_query($conn, "DROP TABLE IF EXISTS $tableName");
|
||||
$stmt = sqlsrv_query($conn, $createQuery);
|
||||
if(!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
die("Creating an encrypted table failed when it shouldn't have!\n");
|
||||
}
|
||||
|
||||
insertValues($conn, $insertQuery, $dataTypes, $testValues);
|
||||
|
||||
if ($count == 0) {
|
||||
|
||||
// Split the data type array, because for some reason we get an error
|
||||
// if the query is too long (>2000 characters)
|
||||
// TODO: This is a known issue, follow up on it.
|
||||
$splitDataTypes = array_chunk($dataTypes, 5);
|
||||
foreach ($splitDataTypes as $split)
|
||||
{
|
||||
$alterQuery = constructAlterQuery($tableName, $colNamesAE, $split, $key, $encryptionType, $slength);
|
||||
|
||||
$stmt = sqlsrv_query($conn, $alterQuery);
|
||||
$encryptionFailed = false;
|
||||
|
||||
if(!$stmt) {
|
||||
if (!isEnclaveEnabled($key)) {
|
||||
$e = sqlsrv_errors();
|
||||
checkErrors($e, array('42000', '33543'));
|
||||
$encryptionFailed = true;
|
||||
continue;
|
||||
} else {
|
||||
print_r(sqlsrv_errors());
|
||||
die("Encrypting failed when it shouldn't have!\n");
|
||||
}
|
||||
} else {
|
||||
if (!isEnclaveEnabled($key)) {
|
||||
die("Encrypting should have failed with key $key and encryption type $encryptionType\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($encryptionFailed) continue;
|
||||
|
||||
if ($count == 0) {
|
||||
testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $thresholds, $length, $key, $encryptionType, 'correct');
|
||||
testPatternMatch($conn, $tableName, $patterns, $dataTypes, $colNames, $key, $encryptionType, 'correct');
|
||||
}
|
||||
++$count;
|
||||
|
||||
if ($key == $targetKey and $encryptionType == $targetType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try re-encrypting the table
|
||||
$encryptionFailed = false;
|
||||
foreach ($splitDataTypes as $split) {
|
||||
$alterQuery = constructAlterQuery($tableName, $colNamesAE, $split, $targetKey, $targetType, $slength);
|
||||
|
||||
$stmt = sqlsrv_query($conn, $alterQuery);
|
||||
if(!$stmt) {
|
||||
if (!isEnclaveEnabled($targetKey)) {
|
||||
$e = sqlsrv_errors();
|
||||
checkErrors($e, array('42000', '33543'));
|
||||
$encryptionFailed = true;
|
||||
continue;
|
||||
} else {
|
||||
print_r(sqlsrv_errors());
|
||||
die("Encrypting failed when it shouldn't have!\n");
|
||||
}
|
||||
} else {
|
||||
if (!isEnclaveEnabled($targetKey)) {
|
||||
die("Encrypting should have failed with key $targetKey and encryption type $targetType\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($encryptionFailed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $thresholds, $length, $targetKey, $targetType, 'correct');
|
||||
testPatternMatch($conn, $tableName, $patterns, $dataTypes, $colNames, $targetKey, $targetType, 'correct');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "Done.\n";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done.
|
71
test/functional/sqlsrv/sqlsrv_aev2_keywords.phpt
Normal file
71
test/functional/sqlsrv/sqlsrv_aev2_keywords.phpt
Normal file
|
@ -0,0 +1,71 @@
|
|||
--TEST--
|
||||
Test various settings for the ColumnEncryption keyword.
|
||||
--DESCRIPTION--
|
||||
For AE v2, the Column Encryption keyword must be set to [protocol]/[attestation URL].
|
||||
If [protocol] is wrong, connection should fail; if the URL is wrong, connection
|
||||
should succeed. This test sets ColumnEncryption to three values:
|
||||
1. Random nonsense, which is interpreted as an incorrect protocol
|
||||
so connection should fail.
|
||||
2. Incorrect protocol with a correct attestation URL, connection should fail.
|
||||
3. Correct protocol and incorrect URL, connection should succeed.
|
||||
--SKIPIF--
|
||||
<?php require("skipif_not_hgs.inc"); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
require_once("AE_v2_values.inc");
|
||||
require_once("sqlsrv_AE_functions.inc");
|
||||
|
||||
// Test with random nonsense. Connection should fail.
|
||||
$options = array('database'=>$database,
|
||||
'uid'=>$userName,
|
||||
'pwd'=>$userPassword,
|
||||
'ColumnEncryption'=>"xyz",
|
||||
);
|
||||
|
||||
$conn = sqlsrv_connect($server, $options);
|
||||
if (!$conn) {
|
||||
$e = sqlsrv_errors();
|
||||
checkErrors($e, array('CE400', '0'));
|
||||
} else {
|
||||
die("Connecting with nonsense should have failed!\n");
|
||||
}
|
||||
|
||||
// Test with incorrect protocol and good attestation URL. Connection should fail.
|
||||
// Insert a rogue 'x' into the protocol part of the attestation.
|
||||
$comma = strpos($attestation, ',');
|
||||
$badProtocol = substr_replace($attestation, 'x', $comma, 0);
|
||||
$options = array('database'=>$database,
|
||||
'uid'=>$userName,
|
||||
'pwd'=>$userPassword,
|
||||
'ColumnEncryption'=>$badProtocol,
|
||||
);
|
||||
|
||||
$conn = sqlsrv_connect($server, $options);
|
||||
if (!$conn) {
|
||||
$e = sqlsrv_errors();
|
||||
checkErrors($e, array('CE400', '0'));
|
||||
} else {
|
||||
die("Connecting with a bad attestation protocol should have failed!\n");
|
||||
}
|
||||
|
||||
// Test with good protocol and incorrect attestation URL. Connection should succeed
|
||||
// because the URL is only checked when an enclave computation is attempted.
|
||||
$badURL = substr_replace($attestation, 'x', $comma+1, 0);
|
||||
$options = array('database'=>$database,
|
||||
'uid'=>$userName,
|
||||
'pwd'=>$userPassword,
|
||||
'ColumnEncryption'=>$badURL,
|
||||
);
|
||||
|
||||
$conn = sqlsrv_connect($server, $options);
|
||||
if (!$conn) {
|
||||
print_r(sqlsrv_errors());
|
||||
die("Connecting with a bad attestation URL should have succeeded!\n");
|
||||
}
|
||||
|
||||
echo "Done.\n";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done.
|
110
test/functional/sqlsrv/sqlsrv_aev2_reencrypt_encrypted.phpt
Normal file
110
test/functional/sqlsrv/sqlsrv_aev2_reencrypt_encrypted.phpt
Normal file
|
@ -0,0 +1,110 @@
|
|||
--TEST--
|
||||
Test rich computations and in place re-encryption with AE v2.
|
||||
--DESCRIPTION--
|
||||
This test cycles through $encryptionTypes and $keys, creating an encrypted table
|
||||
each time, then cycles through $targetTypes and $targetKeys to try re-encrypting
|
||||
the table with different combinations of enclave-enabled and non-enclave keys
|
||||
and encryption types.
|
||||
The sequence of operations is the following:
|
||||
1. Create an encrypted table with two columns for each AE-supported data type, one encrypted and one not encrypted.
|
||||
2. Insert some data.
|
||||
3. Perform rich computations on each AE-enabled column (comparisons and pattern matching) and compare the result
|
||||
to the same query on the corresponding non-AE column for each data type.
|
||||
4. Ensure the two results are the same.
|
||||
5. Re-encrypt the table using new key and/or encryption type.
|
||||
6. Compare computations as in 4. above.
|
||||
--SKIPIF--
|
||||
<?php require("skipif_not_hgs.inc"); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
require_once("AE_v2_values.inc");
|
||||
require_once("sqlsrv_AE_functions.inc");
|
||||
|
||||
$initialAttestation = $attestation;
|
||||
|
||||
// Create a table for each key and encryption type, re-encrypt using each
|
||||
// combination of target key and target encryption
|
||||
foreach ($keys as $key) {
|
||||
foreach ($encryptionTypes as $encryptionType) {
|
||||
|
||||
// $count is used to ensure we only run testCompare and
|
||||
// testPatternMatch once for the initial table
|
||||
$count = 0;
|
||||
$conn = connect($server, $attestation);
|
||||
|
||||
foreach ($targetKeys as $targetKey) {
|
||||
foreach ($targetTypes as $targetType) {
|
||||
|
||||
// Free the encryption cache to avoid spurious 'operand type clash' errors
|
||||
sqlsrv_query($conn, "DBCC FREEPROCCACHE");
|
||||
|
||||
// Create an encrypted table
|
||||
$createQuery = constructAECreateQuery($tableName, $dataTypes, $colNames, $colNamesAE, $slength, $key, $encryptionType);
|
||||
$insertQuery = constructInsertQuery($tableName, $dataTypes, $colNames, $colNamesAE);
|
||||
|
||||
$stmt = sqlsrv_query($conn, "DROP TABLE IF EXISTS $tableName");
|
||||
$stmt = sqlsrv_query($conn, $createQuery);
|
||||
if(!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
die("Creating an encrypted table failed when it shouldn't have!\n");
|
||||
}
|
||||
|
||||
insertValues($conn, $insertQuery, $dataTypes, $testValues);
|
||||
|
||||
if ($count == 0) {
|
||||
testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $thresholds, $length, $key, $encryptionType, 'correct');
|
||||
testPatternMatch($conn, $tableName, $patterns, $dataTypes, $colNames, $key, $encryptionType, 'correct');
|
||||
}
|
||||
++$count;
|
||||
|
||||
if ($key == $targetKey and $encryptionType == $targetType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Split the data type array, because for some reason we get an error
|
||||
// if the query is too long (>2000 characters)
|
||||
// TODO: This is a known issue, follow up on it.
|
||||
$splitDataTypes = array_chunk($dataTypes, 5);
|
||||
$encryptionFailed = false;
|
||||
|
||||
foreach ($splitDataTypes as $split) {
|
||||
|
||||
$alterQuery = constructAlterQuery($tableName, $colNamesAE, $split, $targetKey, $targetType, $slength);
|
||||
$stmt = sqlsrv_query($conn, $alterQuery);
|
||||
|
||||
if(!$stmt) {
|
||||
if (!isEnclaveEnabled($key) or !isEnclaveEnabled($targetKey)) {
|
||||
$e = sqlsrv_errors();
|
||||
checkErrors($e, array('42000', '33543'));
|
||||
$encryptionFailed = true;
|
||||
continue;
|
||||
} else {
|
||||
print_r(sqlsrv_errors());
|
||||
die("Encrypting failed when it shouldn't have! key = $targetKey and type = $targetType\n");
|
||||
}
|
||||
|
||||
continue;
|
||||
} else {
|
||||
if (!isEnclaveEnabled($key) or !isEnclaveEnabled($targetKey)) {
|
||||
die("Encrypting should have failed with key $targetKey and encryption type $encryptionType\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($encryptionFailed) {
|
||||
continue;
|
||||
}
|
||||
|
||||
testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $thresholds, $length, $targetKey, $targetType, 'correct');
|
||||
testPatternMatch($conn, $tableName, $patterns, $dataTypes, $colNames, $targetKey, $targetType, 'correct');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "Done.\n";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done.
|
93
test/functional/sqlsrv/sqlsrv_aev2_wrong_attestation.phpt
Normal file
93
test/functional/sqlsrv/sqlsrv_aev2_wrong_attestation.phpt
Normal file
|
@ -0,0 +1,93 @@
|
|||
--TEST--
|
||||
Try re-encrypting a table with ColumnEncryption set to the wrong attestation URL, which should fail.
|
||||
--DESCRIPTION--
|
||||
This test cycles through $encryptionTypes and $keys, creating an encrypted table
|
||||
each time, then cycles through $targetTypes and $targetKeys to try re-encrypting
|
||||
the table with different combinations of enclave-enabled and non-enclave keys
|
||||
and encryption types.
|
||||
The sequence of operations is the following:
|
||||
1. Connect with correct attestation information.
|
||||
2. Create an encrypted table with two columns for each AE-supported data type, one encrypted and one not encrypted.
|
||||
3. Insert some data.
|
||||
4. Disconnect and reconnect with a faulty attestation URL.
|
||||
5. Test comparison and pattern matching by comparing the results for the encrypted and non-encrypted columns.
|
||||
Equality should work with deterministic encryption as in AE v1, but other computations should fail.
|
||||
6. Try re-encrypting the table. This should fail.
|
||||
--SKIPIF--
|
||||
<?php require("skipif_not_hgs.inc"); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
require_once("AE_v2_values.inc");
|
||||
require_once("sqlsrv_AE_functions.inc");
|
||||
|
||||
$initialAttestation = $attestation;
|
||||
|
||||
// Create a table for each key and encryption type, re-encrypt using each
|
||||
// combination of target key and target encryption
|
||||
foreach ($keys as $key) {
|
||||
foreach ($encryptionTypes as $encryptionType) {
|
||||
|
||||
// $count is used to ensure we only run testCompare and
|
||||
// testPatternMatch once for the initial table
|
||||
$count = 0;
|
||||
|
||||
foreach ($targetKeys as $targetKey) {
|
||||
foreach ($targetTypes as $targetType) {
|
||||
|
||||
$conn = connect($server, $initialAttestation);
|
||||
|
||||
// Create an encrypted table
|
||||
$createQuery = constructAECreateQuery($tableName, $dataTypes, $colNames, $colNamesAE, $slength, $key, $encryptionType);
|
||||
$insertQuery = constructInsertQuery($tableName, $dataTypes, $colNames, $colNamesAE);
|
||||
|
||||
$stmt = sqlsrv_query($conn, "DROP TABLE IF EXISTS $tableName");
|
||||
$stmt = sqlsrv_query($conn, $createQuery);
|
||||
if(!$stmt) {
|
||||
print_r(sqlsrv_errors());
|
||||
die("Creating an encrypted table failed when it shouldn't have!\n");
|
||||
}
|
||||
|
||||
insertValues($conn, $insertQuery, $dataTypes, $testValues);
|
||||
unset($conn);
|
||||
|
||||
// Reconnect with a faulty attestation URL
|
||||
$comma = strpos($attestation, ',');
|
||||
$newAttestation = substr_replace($attestation, 'x', $comma+1, 0);
|
||||
|
||||
$conn = connect($server, $newAttestation);
|
||||
|
||||
if ($count == 0) {
|
||||
testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $thresholds, $length, $key, $encryptionType, 'wrongurl');
|
||||
testPatternMatch($conn, $tableName, $patterns, $dataTypes, $colNames, $key, $encryptionType, 'wrongurl');
|
||||
}
|
||||
++$count;
|
||||
|
||||
if ($key == $targetKey and $encryptionType == $targetType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$alterQuery = constructAlterQuery($tableName, $colNamesAE, $dataTypes, $targetKey, $targetType, $slength);
|
||||
$stmt = sqlsrv_query($conn, $alterQuery);
|
||||
|
||||
if(!$stmt) {
|
||||
if (!isEnclaveEnabled($key) or !isEnclaveEnabled($targetKey)) {
|
||||
$e = sqlsrv_errors();
|
||||
checkErrors($e, array('42000', '33543'));
|
||||
} else {
|
||||
$e = sqlsrv_errors();
|
||||
checkErrors($e, array('CE405', '0'));
|
||||
}
|
||||
} else {
|
||||
die("Encrypting should have failed with key $targetKey and encryption type $targetType\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "Done.\n";
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done.
|
|
@ -1,8 +1,8 @@
|
|||
--TEST--
|
||||
Test the existence of Windows Always Encrypted keys generated in the database setup
|
||||
--DESCRIPTION--
|
||||
This test iterates through the rows of sys.column_master_keys and/or
|
||||
sys.column_encryption_keys to look for the specific column master key and
|
||||
This test iterates through the rows of sys.column_master_keys and/or
|
||||
sys.column_encryption_keys to look for the specific column master key and
|
||||
column encryption key generated in the database setup
|
||||
--SKIPIF--
|
||||
<?php require('skipif_unix.inc'); ?>
|
||||
|
@ -44,8 +44,8 @@ if (AE\IsQualified($conn)) {
|
|||
sqlsrv_free_stmt($stmt);
|
||||
}
|
||||
|
||||
echo "Test Successfully done.\n";
|
||||
echo "Test successfully done.\n";
|
||||
sqlsrv_close($conn);
|
||||
?>
|
||||
--EXPECT--
|
||||
Test Successfully done.
|
||||
Test successfully done.
|
||||
|
|
Loading…
Reference in a new issue