All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.microsoft.sqlserver.jdbc.SQLServerNoneEnclaveProvider Maven / Gradle / Ivy

There is a newer version: 12.8.1.jre11
Show newest version
/*
 * Microsoft JDBC Driver for SQL Server Copyright(c) Microsoft Corporation All rights reserved. This program is made
 * available under the terms of the MIT License. See the LICENSE file in the project root for more information.
 */

package com.microsoft.sqlserver.jdbc;

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;
import java.util.ArrayList;


/**
 * 
 * Provides the implementation of the NONE Enclave Provider. This enclave provider does not use attestation.
 *
 */
public class SQLServerNoneEnclaveProvider implements ISQLServerEnclaveProvider {

    private static final EnclaveSessionCache enclaveCache = new EnclaveSessionCache();

    private NoneAttestationParameters noneParams = null;
    private NoneAttestationResponse noneResponse = null;
    private String attestationUrl = null;
    private EnclaveSession enclaveSession = null;

    /**
     * default constructor
     */
    public SQLServerNoneEnclaveProvider() {}

    @Override
    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);
            }
        }
    }

    @Override
    public ArrayList createEnclaveSession(SQLServerConnection connection, SQLServerStatement statement,
            String userSql, String preparedTypeDefinitions, Parameter[] params,
            ArrayList parameterNames) throws SQLServerException {

        /*
         * for None attestation: enclave does not send public key, and sends an empty attestation info. The only
         * non-trivial content it sends is the session setup info (DH pubkey of enclave).
         */

        // Check if the session exists in our cache
        StringBuilder keyLookup = new StringBuilder(connection.getServerName()).append(connection.getCatalog())
                .append(attestationUrl);
        EnclaveCacheEntry entry = enclaveCache.getSession(keyLookup.toString());
        if (null != entry) {
            this.enclaveSession = entry.getEnclaveSession();
            this.noneParams = (NoneAttestationParameters) entry.getBaseAttestationRequest();
        }
        ArrayList b = describeParameterEncryption(connection, statement, userSql, preparedTypeDefinitions,
                params, parameterNames);
        if (connection.enclaveEstablished()) {
            return b;
        } else if (null != noneResponse && !connection.enclaveEstablished()) {
            try {
                enclaveSession = new EnclaveSession(noneResponse.getSessionID(),
                        noneParams.createSessionSecret(noneResponse.getDHpublicKey()));
                enclaveCache.addEntry(connection.getServerName(), connection.getCatalog(),
                        connection.enclaveAttestationUrl, noneParams, enclaveSession);
            } catch (GeneralSecurityException e) {
                SQLServerException.makeFromDriverError(connection, this, e.getLocalizedMessage(), "0", false);
            }
        }
        return b;
    }

    @Override
    public void invalidateEnclaveSession() {
        if (null != enclaveSession) {
            enclaveCache.removeEntry(enclaveSession);
        }
        enclaveSession = null;
        noneParams = null;
        attestationUrl = null;
    }

    @Override
    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 describeParameterEncryption(SQLServerConnection connection, SQLServerStatement statement,
            String userSql, String preparedTypeDefinitions, Parameter[] params,
            ArrayList parameterNames) throws SQLServerException {
        ArrayList enclaveRequestedCEKs = new ArrayList<>();
        try (PreparedStatement stmt = connection.prepareStatement(connection.enclaveEstablished() ? SDPE1 : SDPE2)) {
            // Check the cache for metadata for Always Encrypted versions 1 and 3, when there are parameters to check.
            if (connection.getServerColumnEncryptionVersion() == ColumnEncryptionVersion.AE_V2 || params == null
                    || params.length == 0 || !ParameterMetaDataCache.getQueryMetadata(params, parameterNames,
                            connection, statement, userSql)) {
                try (ResultSet rs = connection.enclaveEstablished() ? executeSDPEv1(stmt, userSql,
                        preparedTypeDefinitions) : executeSDPEv2(stmt, userSql, preparedTypeDefinitions, noneParams)) {
                    if (null == rs) {
                        // No results. Meaning no parameter.
                        // Should never happen.
                        return enclaveRequestedCEKs;
                    }
                    processSDPEv1(userSql, preparedTypeDefinitions, params, parameterNames, connection, statement, stmt,
                            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();
                            } else {
                                SQLServerException.makeFromDriverError(null, this,
                                        SQLServerException.getErrString("R_UnableRetrieveParameterMetadata"), "0",
                                        false);
                            }
                        }
                    }
                }
            }
        } catch (SQLException | IOException e) {
            if (e instanceof SQLServerException) {
                throw (SQLServerException) e;
            } else {
                throw new SQLServerException(SQLServerException.getErrString("R_UnableRetrieveParameterMetadata"), null,
                        0, e);
            }
        }
        return enclaveRequestedCEKs;
    }
}


/**
 * 
 * Represents the serialization of the request the client sends to the SQL Server while setting up a session.
 *
 */
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();

        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;
    }
}


/**
 * 
 * Represents the deserialization of the byte payload the client receives from the SQL Server while setting up a
 * session.
 *
 */
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);

        if (0 != response.remaining()) {
            SQLServerException.makeFromDriverError(null, this,
                    SQLServerResource.getResource("R_EnclaveResponseLengthError"), "0", false);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy