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

Remade attestation NONE to work in all cases #1942

Merged
merged 19 commits into from
Nov 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@

package com.microsoft.sqlserver.jdbc;

import static java.nio.charset.StandardCharsets.UTF_16LE;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
Expand Down Expand Up @@ -42,11 +39,7 @@ public SQLServerNoneEnclaveProvider() {}
public void getAttestationParameters(String url) throws SQLServerException {
if (null == noneParams) {
attestationUrl = url;
try {
noneParams = new NoneAttestationParameters(attestationUrl);
} catch (IOException e) {
SQLServerException.makeFromDriverError(null, this, e.getLocalizedMessage(), "0", false);
}
noneParams = new NoneAttestationParameters();
}
}

Expand Down Expand Up @@ -100,16 +93,6 @@ public EnclaveSession getEnclaveSession() {
return enclaveSession;
}

private void validateAttestationResponse() throws SQLServerException {
if (null != noneResponse) {
try {
noneResponse.validateDHPublicKey();
} catch (GeneralSecurityException e) {
SQLServerException.makeFromDriverError(null, this, e.getLocalizedMessage(), "0", false);
}
}
}

private ArrayList<byte[]> describeParameterEncryption(SQLServerConnection connection, SQLServerStatement statement,
String userSql, String preparedTypeDefinitions, Parameter[] params,
ArrayList<String> parameterNames) throws SQLServerException {
Expand All @@ -130,11 +113,9 @@ private ArrayList<byte[]> describeParameterEncryption(SQLServerConnection connec
rs, enclaveRequestedCEKs);
// Process the third result set.
if (connection.isAEv2() && stmt.getMoreResults()) {
try (ResultSet hgsRs = stmt.getResultSet()) {
if (hgsRs.next()) {
noneResponse = new NoneAttestationResponse(hgsRs.getBytes(1));
// This validates and establishes the enclave session if valid
validateAttestationResponse();
try (ResultSet noneRs = stmt.getResultSet()) {
if (noneRs.next()) {
noneResponse = new NoneAttestationResponse(noneRs.getBytes(1));
} else {
SQLServerException.makeFromDriverError(null, this,
SQLServerException.getErrString("R_UnableRetrieveParameterMetadata"), "0",
Expand All @@ -147,10 +128,9 @@ private ArrayList<byte[]> describeParameterEncryption(SQLServerConnection connec
} catch (SQLException | IOException e) {
if (e instanceof SQLServerException) {
throw (SQLServerException) e;
} else {
throw new SQLServerException(SQLServerException.getErrString("R_UnableRetrieveParameterMetadata"), null,
0, e);
}
throw new SQLServerException(SQLServerException.getErrString("R_UnableRetrieveParameterMetadata"), null, 0,
e);
}
return enclaveRequestedCEKs;
}
Expand All @@ -165,41 +145,24 @@ private ArrayList<byte[]> describeParameterEncryption(SQLServerConnection connec
class NoneAttestationParameters extends BaseAttestationRequest {

// Type 2 is NONE, sent as Little Endian 0x20000000
private static final byte[] ENCLAVE_TYPE = new byte[] {0x2, 0x0, 0x0, 0x0};
// Nonce length is always 256
private static final byte[] NONCE_LENGTH = new byte[] {0x0, 0x1, 0x0, 0x0};
private final byte[] nonce = new byte[256];

NoneAttestationParameters(String attestationUrl) throws SQLServerException, IOException {
byte[] attestationUrlBytes = (attestationUrl + '\0').getBytes(UTF_16LE);

ByteArrayOutputStream os = new ByteArrayOutputStream();
os.write(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(attestationUrlBytes.length).array());
os.write(attestationUrlBytes);
os.write(NONCE_LENGTH);
new SecureRandom().nextBytes(nonce);
os.write(nonce);
enclaveChallenge = os.toByteArray();
private static byte ENCLAVE_TYPE[] = new byte[] {0x2, 0x0, 0x0, 0x0};

NoneAttestationParameters() throws SQLServerException {
enclaveChallenge = new byte[] {0x0, 0x0, 0x0, 0x0};
initBcryptECDH();
}

@Override
byte[] getBytes() throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
os.write(ENCLAVE_TYPE);
os.write(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(enclaveChallenge.length).array());
os.write(enclaveChallenge);
os.write(ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(ENCLAVE_LENGTH).array());
os.write(ECDH_MAGIC);
os.write(x);
os.write(y);
return os.toByteArray();
}

byte[] getNonce() {
return nonce;
}
}


Expand All @@ -213,42 +176,32 @@ class NoneAttestationResponse extends BaseAttestationResponse {

NoneAttestationResponse(byte[] b) throws SQLServerException {
/*-
* Protocol format:
* 1. Total Size of the attestation blob as UINT
* 2. Size of Enclave RSA public key as UINT
* 3. Size of Attestation token as UINT
* 4. Enclave Type as UINT
* 5. Enclave RSA public key (raw key, of length #2)
* 6. Attestation token (of length #3)
* 7. Size of Session ID was UINT
* 8. Session id value
* 9. Size of enclave ECDH public key
* 10. Enclave ECDH public key (of length #9)
*/
ByteBuffer response = ByteBuffer.wrap(b).order(ByteOrder.LITTLE_ENDIAN);
this.totalSize = response.getInt();
this.identitySize = response.getInt();
this.attestationTokenSize = response.getInt();
this.enclaveType = response.getInt(); // 1 for VBS, 2 for SGX

enclavePK = new byte[identitySize];
byte[] attestationToken = new byte[attestationTokenSize];

response.get(enclavePK, 0, identitySize);
response.get(attestationToken, 0, attestationTokenSize);

this.sessionInfoSize = response.getInt();
response.get(sessionID, 0, 8);
this.DHPKsize = response.getInt();
this.DHPKSsize = response.getInt();

DHpublicKey = new byte[DHPKsize];
publicKeySig = new byte[DHPKSsize];

response.get(DHpublicKey, 0, DHPKsize);
response.get(publicKeySig, 0, DHPKSsize);
* Parse the attestation response.
*
* Total Size of the response - 4B
* Session Info Size - 4B
* Session ID - 8B
* DH Public Key Size - 4B
* DH Public Key Signature Size - 4B
* DH Public Key - DHPKsize bytes
* DH Public Key Signature - DHPKSsize bytes
*/
ByteBuffer response = (null != b) ? ByteBuffer.wrap(b).order(ByteOrder.LITTLE_ENDIAN) : null;
if (null != response) {
this.totalSize = response.getInt();
this.sessionInfoSize = response.getInt();
response.get(sessionID, 0, 8);
this.DHPKsize = response.getInt();
this.DHPKSsize = response.getInt();

DHpublicKey = new byte[DHPKsize];
publicKeySig = new byte[DHPKSsize];

response.get(DHpublicKey, 0, DHPKsize);
response.get(publicKeySig, 0, DHPKSsize);
}

if (0 != response.remaining()) {
if (null == response || 0 != response.remaining()) {
SQLServerException.makeFromDriverError(null, this,
SQLServerResource.getResource("R_EnclaveResponseLengthError"), "0", false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,8 @@ public static void testBasicConnection(String cString, String protocol) throws E
}

/**
* Tests invalid connection property combinations.
* Tests invalid connection property combinations. NONE protocol is allowed without an attestation URL, and so
* an exclusion is defined for NONE.
*
* @throws Exception
*/
Expand All @@ -262,9 +263,11 @@ public static void testInvalidProperties(String serverName, String url, String p
testInvalidProperties(TestUtils.removeProperty(connectionStringEnclave, "enclaveAttestationProtocol"),
"R_enclavePropertiesError");

// enclaveAttestationProtocol without enclaveAttestationUrl
testInvalidProperties(TestUtils.addOrOverrideProperty(connectionStringEnclave, "enclaveAttestationUrl", ""),
"R_enclavePropertiesError");
// enclaveAttestationProtocol without enclaveAttestationUrl (given that it is not NONE)
if (!String.valueOf(AttestationProtocol.NONE).equals(protocol)) {
testInvalidProperties(TestUtils.addOrOverrideProperty(connectionStringEnclave, "enclaveAttestationUrl", ""),
"R_enclavePropertiesError");
}

// bad enclaveAttestationProtocol
testInvalidProperties(
Expand Down