Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Always Encrypted v2 support #1045

Merged
merged 21 commits into from
Oct 31, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 34 additions & 4 deletions test/functional/pdo_sqlsrv/pdo_AE_functions.inc
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ function connect($server, $attestation_info)

// 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)
{
david-puglielli marked this conversation as resolved.
Show resolved Hide resolved
$query = "CREATE TABLE ".$tableName." (\n ";
Expand All @@ -59,13 +64,25 @@ function constructCreateQuery($tableName, $dataTypes, $colNames, $colNamesAE, $s
}
}

// Remove the ", \n " from the end of the query or the comma will cause a syntax error
$query = substr($query, 0, -7)."\n)";

david-puglielli marked this conversation as resolved.
Show resolved Hide resolved
return $query;
}

// The ALTER TABLE query encrypts columns. Each ALTER COLUMN directive must
// be preceded by ALTER TABLE
// 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)
david-puglielli marked this conversation as resolved.
Show resolved Hide resolved
{
$query = '';
Expand All @@ -87,6 +104,12 @@ function constructAlterQuery($tableName, $colNames, $dataTypes, $key, $encryptio

// 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 ";
Expand All @@ -106,6 +129,7 @@ function constructAECreateQuery($tableName, $dataTypes, $colNames, $colNamesAE,
}
}

// Remove the ",\n " from the end of the query or the comma will cause a syntax error
$query = substr($query, 0, -6)."\n)";

david-puglielli marked this conversation as resolved.
Show resolved Hide resolved
return $query;
Expand All @@ -125,6 +149,7 @@ function constructInsertQuery($tableName, &$dataTypes, &$colNames, &$colNamesAE)
$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).")";
david-puglielli marked this conversation as resolved.
Show resolved Hide resolved

Expand Down Expand Up @@ -275,7 +300,7 @@ function compareResults($AEstmt, $nonAEstmt, $key, $encryptionType, $attestation
// results are identical
// Arguments:
// resource $conn: The connection
// string $tableName: Thable name
// 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
Expand All @@ -288,7 +313,8 @@ function testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $th
foreach ($comparisons as $comparison) {
foreach ($dataTypes as $type) {

// Unicode data types the correct collation. Otherwise we get different
// 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);
david-puglielli marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -319,7 +345,7 @@ function testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $th
// results are identical
// Arguments:
// resource $conn: The connection
// string $tableName: Thable name
// 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
Expand All @@ -346,6 +372,10 @@ function testPatternMatch($conn, $tableName, $patterns, $dataTypes, $colNames, $

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);
yitam marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
27 changes: 15 additions & 12 deletions test/functional/pdo_sqlsrv/pdo_aev2_ce_enabled.phpt
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
--TEST--
Test rich computations and in place encryption with AE v2.
Try re-encrypting a table with ColumnEncryption set to 'enabled', which should fail.
--DESCRIPTION--
This test does the following:
1. Connect with correct attestation information.
1. Create an encrypted table with two columns for each AE-supported data type.
2. Insert some data.
3. Disconnect and reconnect with ColumnEncryption set to 'enabled'.
4. Test comparison and pattern matching. Equality should work with deterministic encryption as in AE v1, but other computations should fail.
5. Try re-encrypting the table. This should fail.
2. Create an encrypted table with two columns for each AE-supported data type.
3. Insert some data.
4. Disconnect and reconnect with ColumnEncryption set to 'enabled'.
5. Test comparison and pattern matching. 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
include("MsSetup.inc");
include("AE_v2_values.inc");
include("pdo_AE_functions.inc");
require_once("MsSetup.inc");
require_once("AE_v2_values.inc");
require_once("pdo_AE_functions.inc");

david-puglielli marked this conversation as resolved.
Show resolved Hide resolved
$initialAttestation = $attestation;

Expand Down Expand Up @@ -50,12 +50,15 @@ foreach ($keys as $key) {
$newAttestation = 'enabled';
$conn = connect($server, $newAttestation);

if ($count == 0) testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $thresholds, $key, $encryptionType, 'enabled');
if ($count == 0) testPatternMatch($conn, $tableName, $patterns, $dataTypes, $colNames, $key, $encryptionType, 'enabled');
if ($count == 0) {
testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $thresholds, $key, $encryptionType, 'enabled');
testPatternMatch($conn, $tableName, $patterns, $dataTypes, $colNames, $key, $encryptionType, 'enabled');
}
++$count;
Copy link
Contributor

Choose a reason for hiding this comment

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

Use curly brackets for if else statements
Also add brief comments to indicate what you expect - succeed or fail (readers don't have to scroll back up to check)


if ($key == $targetKey and $encryptionType == $targetType)
if ($key == $targetKey and $encryptionType == $targetType) {
continue;
}

$alterQuery = constructAlterQuery($tableName, $colNamesAE, $dataTypes, $targetKey, $targetType, $slength);

Expand Down
37 changes: 22 additions & 15 deletions test/functional/pdo_sqlsrv/pdo_aev2_encrypt_plaintext.phpt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
--TEST--
Test rich computations and in place encryption with AE v2.
Test rich computations and in-place encryption of plaintext with AE v2.
--DESCRIPTION--
This test does the following:
1. Create a table in plaintext with two columns for each AE-supported data type.
Expand All @@ -13,9 +13,9 @@ This test does the following:
<?php require("skipif_not_hgs.inc"); ?>
--FILE--
<?php
include("MsSetup.inc");
include("AE_v2_values.inc");
include("pdo_AE_functions.inc");
require_once("MsSetup.inc");
require_once("AE_v2_values.inc");
require_once("pdo_AE_functions.inc");

david-puglielli marked this conversation as resolved.
Show resolved Hide resolved
$initialAttestation = $attestation;

Expand All @@ -32,6 +32,7 @@ foreach ($keys as $key) {
foreach ($targetKeys as $targetKey) {
foreach ($targetTypes as $targetType) {

// Free the encryption cache to avoid spurious 'operand type clash' errors
$conn->query("DBCC FREEPROCCACHE");
david-puglielli marked this conversation as resolved.
Show resolved Hide resolved

// Create and populate a non-encrypted table
Expand All @@ -48,14 +49,14 @@ foreach ($keys as $key) {

insertValues($conn, $insertQuery, $dataTypes, $testValues);

if ($count == 0)
{
if ($count == 0) {
// Split the data type array, because for some reason we get an error
// if the query is too long (>2000 characters)
yitam marked this conversation as resolved.
Show resolved Hide resolved
// 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);
$encryption_failed = false;
$encryptionFailed = false;

try {
$stmt = $conn->query($alterQuery);
Expand All @@ -66,7 +67,7 @@ foreach ($keys as $key) {
if (!isEnclaveEnabled($key)) {
$e = $error->errorInfo;
checkErrors($e, array('42000', '33543'));
$encryption_failed = true;
$encryptionFailed = true;
continue;
} else {
print_r($error);
Expand All @@ -76,19 +77,22 @@ foreach ($keys as $key) {
}
}

if ($encryption_failed) continue;
if ($encryptionFailed) continue;

yitam marked this conversation as resolved.
Show resolved Hide resolved
if ($count == 0) testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $thresholds, $key, $encryptionType, 'correct');
if ($count == 0) testPatternMatch($conn, $tableName, $patterns, $dataTypes, $colNames, $key, $encryptionType, 'correct');
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)
if ($key == $targetKey and $encryptionType == $targetType) {
continue;
}

// Try re-encrypting the table
foreach ($splitDataTypes as $split) {
$alterQuery = constructAlterQuery($tableName, $colNamesAE, $split, $targetKey, $targetType, $slength);
$encryption_failed = false;
$encryptionFailed = false;

try {
$stmt = $conn->query($alterQuery);
Expand All @@ -99,7 +103,7 @@ foreach ($keys as $key) {
if (!isEnclaveEnabled($targetKey)) {
$e = $error->errorInfo;
checkErrors($e, array('42000', '33543'));
$encryption_failed = true;
$encryptionFailed = true;
continue;
} else {
print_r($error);
Expand All @@ -108,7 +112,10 @@ foreach ($keys as $key) {
}
}

if ($encryption_failed) continue;
if ($encryptionFailed) {
continue;
}

testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $thresholds, $targetKey, $targetType, 'correct');
testPatternMatch($conn, $tableName, $patterns, $dataTypes, $colNames, $targetKey, $targetType, 'correct');
}
Expand Down
18 changes: 10 additions & 8 deletions test/functional/pdo_sqlsrv/pdo_aev2_keywords.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
Test ColumnEncryption values.
--DESCRIPTION--
This test checks that connection fails when ColumnEncryption is set to nonsense,
or when it is set to a bad protocol. Then it checks that connection succeeds when
the attestation URL is bad.
or when it is set to an incorrect protocol. Then it checks that connection succeeds when
the attestation URL is incorrect.
--SKIPIF--
<?php require("skipif_not_hgs.inc"); ?>
--FILE--
<?php
include("MsSetup.inc");
include("AE_v2_values.inc");
include("pdo_AE_functions.inc");
require_once("MsSetup.inc");
require_once("AE_v2_values.inc");
require_once("pdo_AE_functions.inc");

david-puglielli marked this conversation as resolved.
Show resolved Hide resolved
// Test with random nonsense. Connection should fail.
$options = "sqlsrv:Server=$server;database=$databaseName;ColumnEncryption=xyz";
Expand All @@ -21,7 +23,7 @@ try {
checkErrors($e, array('CE400', '0'));
}

// Test with bad protocol and good attestation URL. Connection should fail.
// 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);
Expand All @@ -35,7 +37,7 @@ try {
checkErrors($e, array('CE400', '0'));
}

// Test with good protocol and bad attestation URL. Connection should succeed
// 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";
Expand All @@ -51,4 +53,4 @@ echo "Done.\n";

?>
--EXPECT--
Done.
Done.
27 changes: 17 additions & 10 deletions test/functional/pdo_sqlsrv/pdo_aev2_reencrypt_encrypted.phpt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
--TEST--
Test rich computations and in place encryption with AE v2.
Test rich computations and in place re-encryption with AE v2.
--DESCRIPTION--
This test does the following:
1. Create an encrypted table with two columns for each AE-supported data type, one encrypted and one not encrypted.
Expand All @@ -12,9 +12,9 @@ This test does the following:
<?php require("skipif_not_hgs.inc"); ?>
--FILE--
<?php
include("MsSetup.inc");
include("AE_v2_values.inc");
include("pdo_AE_functions.inc");
require_once("MsSetup.inc");
require_once("AE_v2_values.inc");
require_once("pdo_AE_functions.inc");

$initialAttestation = $attestation;

Expand All @@ -31,6 +31,7 @@ foreach ($keys as $key) {
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
Expand All @@ -47,17 +48,21 @@ foreach ($keys as $key) {

insertValues($conn, $insertQuery, $dataTypes, $testValues);

if ($count == 0) testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $thresholds, $key, $encryptionType, 'correct');
if ($count == 0) testPatternMatch($conn, $tableName, $patterns, $dataTypes, $colNames, $key, $encryptionType, 'correct');
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)
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);
$encryption_failed = false;
$encryptionFailed = false;
foreach ($splitDataTypes as $split) {
$alterQuery = constructAlterQuery($tableName, $colNamesAE, $split, $targetKey, $targetType, $slength);

Expand All @@ -70,7 +75,7 @@ foreach ($keys as $key) {
if (!isEnclaveEnabled($key) or !isEnclaveEnabled($targetKey)) {
$e = $error->errorInfo;
checkErrors($e, array('42000', '33543'));
$encryption_failed = true;
$encryptionFailed = true;
continue;
} else {
print_r($error);
Expand All @@ -81,7 +86,9 @@ foreach ($keys as $key) {
}
}

if ($encryption_failed) continue;
if ($encryptionFailed) {
continue;
}

testCompare($conn, $tableName, $comparisons, $dataTypes, $colNames, $thresholds, $targetKey, $targetType, 'correct');
testPatternMatch($conn, $tableName, $patterns, $dataTypes, $colNames, $targetKey, $targetType, 'correct');
Expand Down
Loading