Sample Code

Windows Driver Samples/ CDROM Storage Class Driver/ C++/ src/ aacs.c/

/*--

Copyright (C) Microsoft Corporation. All rights reserved.

Module Name:

    aacs.c

Abstract:

    The CDROM class driver implementation of handling AACS IOCTLs.

Environment:

    kernel mode only

Notes:


Revision History:

--*/

#include "stddef.h"
#include "string.h"

#include "ntddk.h"
#include "ntddstor.h"
#include "cdrom.h"
#include "ioctl.h"
#include "scratch.h"

#ifdef DEBUG_USE_WPP
#include "aacs.tmh"
#endif

#ifdef ALLOC_PRAGMA

#pragma alloc_text(PAGE, DeviceHandleAacsReadMediaKeyBlock)
#pragma alloc_text(PAGE, DeviceHandleAacsStartSession)
#pragma alloc_text(PAGE, DeviceHandleAacsEndSession)
#pragma alloc_text(PAGE, DeviceHandleAacsSendCertificate)
#pragma alloc_text(PAGE, DeviceHandleAacsGetCertificate)
#pragma alloc_text(PAGE, DeviceHandleAacsGetChallengeKey)
#pragma alloc_text(PAGE, DeviceHandleAacsReadSerialNumber)
#pragma alloc_text(PAGE, DeviceHandleAacsReadMediaId)
#pragma alloc_text(PAGE, DeviceHandleAacsReadBindingNonce)
#pragma alloc_text(PAGE, DeviceHandleAacsGenerateBindingNonce)
#pragma alloc_text(PAGE, DeviceHandleReadVolumeId)
#pragma alloc_text(PAGE, DeviceHandleSendChallengeKey)

#endif

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleAacsReadMediaKeyBlock(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
    _In_  WDFREQUEST               Request, 
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:
    This routine is used to process IOCTLs:
        IOCTL_AACS_READ_MEDIA_KEY_BLOCK_SIZE
        IOCTL_AACS_READ_MEDIA_KEY_BLOCK
Arguments:
    DeviceExtension - device context

    Request - the request that will be formatted

    RequestParameters - request parameter structur

    DataLength - data transferred length

Return Value:
    NTSTATUS

  --*/
{
    NTSTATUS            status = STATUS_SUCCESS;
    PAACS_LAYER_NUMBER  layerNumber = NULL;
    PVOID               outputBuffer = NULL;
    ULONG               transferSize = sizeof(READ_DVD_STRUCTURES_HEADER);

    PAGED_CODE();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           (PVOID*)&layerNumber,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                (PVOID*)&outputBuffer,
                                                NULL);
    }

    if (NT_SUCCESS(status))
    {
        if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_AACS_READ_MEDIA_KEY_BLOCK)
        {
            // maximum size for this transfer is one pack + header
            transferSize += AACS_MKB_PACK_SIZE; 
        }

        if (transferSize > DeviceExtension->ScratchContext.ScratchBufferSize)
        {
            // rare case. normally the size of scratch buffer is 64k.
            status = STATUS_INTERNAL_ERROR;
        }
    }

    if (NT_SUCCESS(status))
    {
        UCHAR                       rmdBlockNumber = 0;
        BOOLEAN                     sendChangedCommand = TRUE;
        BOOLEAN                     shouldRetry = TRUE;
        CDB                         cdb;

        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        cdb.READ_DVD_STRUCTURE.OperationCode = SCSIOP_READ_DVD_STRUCTURE;
        // cdb->AsByte[1] = 0x01; // AACS sub-command not required for this

        cdb.READ_DVD_STRUCTURE.LayerNumber = (UCHAR)(*layerNumber);
        cdb.READ_DVD_STRUCTURE.Format = 0x83; // MKB
        cdb.READ_DVD_STRUCTURE.AllocationLength[0] = (UCHAR)(transferSize >> (8*1));
        cdb.READ_DVD_STRUCTURE.AllocationLength[1] = (UCHAR)(transferSize >> (8*0));

        while (sendChangedCommand)
        {
            // RMDBlockNumber is set to zero....
            // RMDBlockNumber[3] maybe changed for other blocks.
            cdb.READ_DVD_STRUCTURE.RMDBlockNumber[3] = rmdBlockNumber;
       
            if (shouldRetry)
            {
                status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, TRUE, &cdb, 12);
            }

    #ifdef ENABLE_AACS_TESTING
            if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_AACS_READ_MEDIA_KEY_BLOCK_SIZE)
            {
                static const UCHAR results[] = { 0x80, 0x02, 0x00, 0x02 };
                RtlCopyMemory(DeviceExtension->ScratchContext.ScratchBuffer, results, SIZEOF_ARRAY(results));
                status = STATUS_SUCCESS;
            }
            else if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_AACS_READ_MEDIA_KEY_BLOCK)
            {
                static const UCHAR results[] = { 0x80, 0x02, 0x00, 0x02 };
                static const UCHAR defaultFill = 0x30; // '0'
                RtlFillMemory(DeviceExtension->ScratchContext.ScratchBuffer, 0x8004, defaultFill);
                RtlCopyMemory(DeviceExtension->ScratchContext.ScratchBuffer, results, SIZEOF_ARRAY(results));
                status = STATUS_SUCCESS;
            }
    #endif //ENABLE_AACS_TESTING

            if (NT_SUCCESS(status))
            {
                // command succeeded, process data...
                PDVD_DESCRIPTOR_HEADER  header = DeviceExtension->ScratchContext.ScratchBuffer;
                UCHAR                   thisPackNumber = cdb.READ_DVD_STRUCTURE.RMDBlockNumber[3];
                UCHAR                   otherPacks = header->Reserved[1];

                // validate and zero-base the otherPacks
                if (otherPacks == 0)
                {
                    TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_INIT,
                                "AACS: Device is reporting zero total packs (invalid)\n"));
                    *DataLength = 0;
                    status = STATUS_IO_DEVICE_ERROR;
                }
                else
                {
                    otherPacks--;

                    if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_AACS_READ_MEDIA_KEY_BLOCK_SIZE)
                    {
                        // if not already requested last pack, do so now
                        if (otherPacks != thisPackNumber)
                        {
                            // re-send the command for the other pack number.
                            // this is safe here because NT_SUCCESS() is TRUE,
                            // and all the rest of this routine does is SetHardError()
                            // and release of resources we're still about to use.

                            // re-zero the output buffer
                            RtlZeroMemory(DeviceExtension->ScratchContext.ScratchBuffer, sizeof(READ_DVD_STRUCTURES_HEADER));

                            // modify the CDB to get the very last pack of the MKB
                            rmdBlockNumber = otherPacks;

                            transferSize = sizeof(READ_DVD_STRUCTURES_HEADER);

                            // keep items clean
                            ScratchBuffer_ResetItems(DeviceExtension, TRUE);

                            // make sure the loop will be executed for modified command.
                            sendChangedCommand = TRUE;
                            shouldRetry = TRUE;
                        }
                        else
                        {
                            // this request already got the last pack
                            // so just interpret the data
                            REVERSE_SHORT(&header->Length);
                            if (header->Length < sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length))
                            {
                                *DataLength = 0;
                                status = STATUS_IO_DEVICE_ERROR;
                            }
                            else
                            {
                                ULONG totalSize = header->Length;
                                // subtract out any remaining bytes in the header
                                // to get the number of usable bytes in this pack
                                totalSize -= sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length);
                                totalSize += otherPacks * AACS_MKB_PACK_SIZE;

                                // save the result and complete the request
                                *((PULONG)outputBuffer) = totalSize;
                                *DataLength = sizeof(ULONG);
                                status = STATUS_SUCCESS;
                            }
                            // This will exit the loop of sendChangedCommand
                            sendChangedCommand = FALSE;
                            shouldRetry = FALSE;
                        }
                    }
                    else if (RequestParameters.Parameters.DeviceIoControl.IoControlCode == IOCTL_AACS_READ_MEDIA_KEY_BLOCK)
                    {
                        // make length field native byte ordering
                        REVERSE_SHORT(&header->Length);

                        // exit if getting invalid data from the drive
                        if (header->Length < sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length))
                        {
                            *DataLength = 0;
                            status = STATUS_IO_DEVICE_ERROR;
                        }
                        else
                        {
                            // success, how many bytes to copy for this pack?
                            ULONG totalSize = header->Length;
                            size_t originalBufferSize;

                            // subtract out any remaining bytes in the header
                            // to get the number of usable bytes in this pack
                            totalSize -= sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length);

                            // if not the final pack, this should be a full transfer per spec
                            NT_ASSERT( (totalSize == AACS_MKB_PACK_SIZE) || (thisPackNumber == otherPacks) );

                            // validate the user's buffer is large enough to accept the full data
                            originalBufferSize = RequestParameters.Parameters.DeviceIoControl.OutputBufferLength;

                            if (originalBufferSize < (totalSize + (AACS_MKB_PACK_SIZE*thisPackNumber)))
                            {
                                // just return a slightly bigger-than-normal size
                                *DataLength = (otherPacks + 1)*AACS_MKB_PACK_SIZE;
                                status = STATUS_BUFFER_TOO_SMALL;
                            }
                            else
                            {
                                PUCHAR whereToCopy;
                                // determine where to copy to the user's memory
                                whereToCopy = outputBuffer;
                                whereToCopy += AACS_MKB_PACK_SIZE * thisPackNumber;
                
                                RtlCopyMemory(whereToCopy, header->Data, totalSize);

                                // update the Information field here because we already
                                // have calculated the size of the block
                                *DataLength = totalSize + (AACS_MKB_PACK_SIZE * thisPackNumber);
                                status = STATUS_SUCCESS;

                                // if there are more packs to get from the device, send it again....
                                if (thisPackNumber != otherPacks)
                                {
                                    // re-send the command for the next pack number.
                                    // this is safe here because NT_SUCCESS() is TRUE,
                                    // and all the rest of this routine does is SetHardError()
                                    // and release of resources we're still about to use.

                                    // re-zero the output buffer
                                    RtlZeroMemory(DeviceExtension->ScratchContext.ScratchBuffer, sizeof(READ_DVD_STRUCTURES_HEADER));

                                    // modify the CDB to get the next pack of the MKB
                                    rmdBlockNumber = cdb.READ_DVD_STRUCTURE.RMDBlockNumber[3]++;

                                    // modify the SRB to be resent
                                    // 
                                    transferSize = AACS_MKB_PACK_SIZE + sizeof(READ_DVD_STRUCTURES_HEADER);

                                    // keep items clean
                                    ScratchBuffer_ResetItems(DeviceExtension, FALSE);

                                    // make sure the loop will be executed for modified command.
                                    sendChangedCommand = TRUE;
                                    shouldRetry = TRUE;
                                }
                                else
                                {
                                    // else, that was the end of the transfer, so just complete the request
                                    sendChangedCommand = FALSE;
                                }
                            }
                        }

                    } // end of IOCTL_AACS_READ_MEDIA_KEY_BLOCK
                }
            } // end of NT_SUCCESS(status)

            if (!NT_SUCCESS(status))
            {
                // command failed.
                sendChangedCommand = FALSE;
            }
        } //end of while (sendChangedCommand)

        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleAacsStartSession(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
    _In_  WDFREQUEST               Request, 
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:
    This routine is used to process IOCTL:
        IOCTL_AACS_START_SESSION
Arguments:
    DeviceExtension - device context

    Request - the request that will be formatted

    RequestParameters - request parameter structur

    DataLength - data transferred length

Return Value:
    NTSTATUS

  --*/
{
    //AacsGetAgid

    NTSTATUS        status = STATUS_SUCCESS;
    PDVD_SESSION_ID sessionId = NULL;

    PAGED_CODE();

    *DataLength = 0;

    status = WdfRequestRetrieveOutputBuffer(Request,
                                            RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                            (PVOID*)&sessionId,
                                            NULL);

    if (NT_SUCCESS(status))
    {
        ULONG   dataTransferLength = sizeof(CDVD_KEY_HEADER) + sizeof(CDVD_REPORT_AGID_DATA);
        CDB     cdb;

        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        cdb.REPORT_KEY.OperationCode = SCSIOP_REPORT_KEY;
        cdb.AsByte[7] = 0x02; // AACS key class
        cdb.REPORT_KEY.AllocationLength[0] = (UCHAR)(dataTransferLength >> (8*1));
        cdb.REPORT_KEY.AllocationLength[1] = (UCHAR)(dataTransferLength >> (8*0));
        cdb.REPORT_KEY.KeyFormat = 0x00; // DVD_REPORT_AGID?

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, dataTransferLength, TRUE, &cdb, 12);

#ifdef ENABLE_AACS_TESTING
        static const UCHAR results[] = { 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0 };
        RtlCopyMemory(DeviceExtension->ScratchContext.ScratchBuffer, results, SIZEOF_ARRAY(results));
        status = STATUS_SUCCESS;
#endif
        if (NT_SUCCESS(status))
        {
            PCDVD_KEY_HEADER        keyHeader = DeviceExtension->ScratchContext.ScratchBuffer;
            PCDVD_REPORT_AGID_DATA  keyData = (PCDVD_REPORT_AGID_DATA)keyHeader->Data;

            *sessionId = (DVD_SESSION_ID)(keyData->AGID);
            *DataLength = sizeof(DVD_SESSION_ID);
        }

        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleAacsEndSession(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
    _In_  WDFREQUEST               Request, 
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:
    This routine is used to process IOCTL:
        IOCTL_AACS_END_SESSION
Arguments:
    DeviceExtension - device context

    Request - the request that will be formatted

    RequestParameters - request parameter structur

    DataLength - data transferred length

Return Value:
    NTSTATUS

  --*/
{
    //AacsReleaseAgid

    NTSTATUS        status = STATUS_SUCCESS;
    PDVD_SESSION_ID sessionId = NULL;

    PAGED_CODE();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           (PVOID*)&sessionId,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        ULONG                       transferSize = 0;
        CDB                         cdb;
        DVD_SESSION_ID              currentSession = 0;
        DVD_SESSION_ID              limitSession = 0;

        if(*sessionId == DVD_END_ALL_SESSIONS) 
        {
            currentSession = 0;
            limitSession = MAX_COPY_PROTECT_AGID - 1;
        }
        else
        {
            currentSession = *sessionId;
            limitSession = *sessionId;
        }

        ScratchBuffer_BeginUse(DeviceExtension);

        do
        {
            RtlZeroMemory(&cdb, sizeof(CDB));
            // Set up the CDB
            cdb.SEND_KEY.OperationCode = SCSIOP_SEND_KEY;
            cdb.AsByte[7] = 0x02; // AACS key class
            cdb.SEND_KEY.AGID = (UCHAR)(currentSession);
            cdb.SEND_KEY.KeyFormat = DVD_INVALIDATE_AGID;

            status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, transferSize, FALSE, &cdb, 12);
            
            currentSession++;
        } while ((currentSession <= limitSession) && NT_SUCCESS(status));

#ifdef ENABLE_AACS_TESTING
        status = STATUS_SUCCESS;
#endif

        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleAacsSendCertificate(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
    _In_  WDFREQUEST               Request, 
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:
    This routine is used to process IOCTL:
        IOCTL_AACS_SEND_CERTIFICATE
Arguments:
    DeviceExtension - device context

    Request - the request that will be formatted

    RequestParameters - request parameter structur

    DataLength - data transferred length

Return Value:
    NTSTATUS

  --*/
{
    //AacsSendHostCertificate

    NTSTATUS                status = STATUS_SUCCESS;
    PAACS_SEND_CERTIFICATE  input = NULL;

    PAGED_CODE();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           (PVOID*)&input,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        ULONG   dataTransferLength = sizeof(CDVD_KEY_HEADER) + sizeof(AACS_CERTIFICATE);
        CDB     cdb;

        ScratchBuffer_BeginUse(DeviceExtension);

        // copy the input buffer to the data buffer for the transfer
        {
            PCDVD_KEY_HEADER header = (PCDVD_KEY_HEADER)DeviceExtension->ScratchContext.ScratchBuffer;
            ULONG            tmp = dataTransferLength;

            tmp -= RTL_SIZEOF_THROUGH_FIELD(CDVD_KEY_HEADER, DataLength);

            header->DataLength[0] = (UCHAR)(tmp >> (8*1));
            header->DataLength[1] = (UCHAR)(tmp >> (8*0));
            RtlCopyMemory(header->Data, &(input->Certificate), sizeof(AACS_CERTIFICATE));
        }

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        cdb.SEND_KEY.OperationCode = SCSIOP_SEND_KEY;
        cdb.AsByte[7] = 0x02; // AACS key class
        cdb.SEND_KEY.ParameterListLength[0] = (UCHAR)(dataTransferLength >> (8*1));
        cdb.SEND_KEY.ParameterListLength[1] = (UCHAR)(dataTransferLength >> (8*0));
        cdb.SEND_KEY.AGID = (UCHAR)( input->SessionId );
        cdb.SEND_KEY.KeyFormat = 0x01; // Send Host Challenge Certificate

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, dataTransferLength, FALSE, &cdb, 12);

#ifdef ENABLE_AACS_TESTING
        status = STATUS_SUCCESS;
#endif
        if (NT_SUCCESS(status))
        {
            *DataLength = 0;
        }

        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleAacsGetCertificate(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
    _In_  WDFREQUEST               Request, 
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:
    This routine is used to process IOCTL:
        IOCTL_AACS_GET_CERTIFICATE
Arguments:
    DeviceExtension - device context

    Request - the request that will be formatted

    RequestParameters - request parameter structur

    DataLength - data transferred length

Return Value:
    NTSTATUS

  --*/
{
    //AacsGetDriveCertificate

    NTSTATUS        status = STATUS_SUCCESS;
    PDVD_SESSION_ID input = NULL;
    PVOID           outputBuffer = NULL;

    PAGED_CODE();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           (PVOID*)&input,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                (PVOID*)&outputBuffer,
                                                NULL);
    }

    if (NT_SUCCESS(status))
    {
        ULONG   dataTransferLength = sizeof(CDVD_KEY_HEADER) + sizeof(AACS_CERTIFICATE);
        CDB     cdb;

        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        cdb.REPORT_KEY.OperationCode = SCSIOP_REPORT_KEY;
        cdb.AsByte[7] = 0x02; // AACS key class
        cdb.REPORT_KEY.AllocationLength[0] = (UCHAR)(dataTransferLength >> (8*1));
        cdb.REPORT_KEY.AllocationLength[1] = (UCHAR)(dataTransferLength >> (8*0));
        cdb.REPORT_KEY.AGID = (UCHAR)(*input);
        cdb.REPORT_KEY.KeyFormat = 0x01; // Return a drive certificate challenge

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, dataTransferLength, TRUE, &cdb, 12);

#ifdef ENABLE_AACS_TESTING
            static const UCHAR results[] = { 0x00, 0x72, 0x00, 0x00 };
            static const UCHAR defaultFill = 0x31; // '1'
            RtlFillMemory(DeviceExtension->ScratchContext.ScratchBuffer, 0x0074, defaultFill);
            RtlCopyMemory(DeviceExtension->ScratchContext.ScratchBuffer, results, SIZEOF_ARRAY(results));
            status = STATUS_SUCCESS;
#endif
        if (NT_SUCCESS(status))
        {
            PDVD_DESCRIPTOR_HEADER  header = DeviceExtension->ScratchContext.ScratchBuffer;
            ULONG                   dataLengthToCopy = sizeof(AACS_CERTIFICATE);

            // make length field native byte ordering
            REVERSE_SHORT(&header->Length);

            // exit if getting invalid data from the drive
            if (header->Length < (sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length)))
            {
                *DataLength = 0;
                status = STATUS_IO_DEVICE_ERROR;
            }

            if (NT_SUCCESS(status))
            {
                // adjust data length to reflect only the addition data
                header->Length -= sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length);

                // exit if the drive is returning an unexpected data size
                if (header->Length != dataLengthToCopy)
                {
                    *DataLength = 0;
                    status = STATUS_IO_DEVICE_ERROR;
                }
            }

            if (NT_SUCCESS(status))
            {
                // else copy the data to the user's buffer
                RtlCopyMemory(outputBuffer, header->Data, dataLengthToCopy);
                *DataLength = dataLengthToCopy;
            }
        }

        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleAacsGetChallengeKey(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
    _In_  WDFREQUEST               Request, 
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:
    This routine is used to process IOCTL:
        IOCTL_AACS_GET_CHALLENGE_KEY
Arguments:
    DeviceExtension - device context

    Request - the request that will be formatted

    RequestParameters - request parameter structur

    DataLength - data transferred length

Return Value:
    NTSTATUS

  --*/
{
    //AacsGetChallengeKey

    NTSTATUS        status = STATUS_SUCCESS;
    PDVD_SESSION_ID input = NULL;
    PVOID           outputBuffer = NULL;

    PAGED_CODE();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           (PVOID*)&input,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                (PVOID*)&outputBuffer,
                                                NULL);
    }

    if (NT_SUCCESS(status))
    {
        ULONG   dataTransferLength = sizeof(CDVD_KEY_HEADER) + sizeof(AACS_CHALLENGE_KEY);
        CDB     cdb;

        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        cdb.REPORT_KEY.OperationCode = SCSIOP_REPORT_KEY;
        cdb.AsByte[7] = 0x02; // AACS key class
        cdb.REPORT_KEY.AllocationLength[0] = (UCHAR)(dataTransferLength >> (8*1));
        cdb.REPORT_KEY.AllocationLength[1] = (UCHAR)(dataTransferLength >> (8*0));
        cdb.REPORT_KEY.AGID = (UCHAR)(*input);
        cdb.REPORT_KEY.KeyFormat = 0x02; // Return a drive certificate challenge

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, dataTransferLength, TRUE, &cdb, 12);

#ifdef ENABLE_AACS_TESTING
            static const UCHAR results[] = { 0x00, 0x52, 0x00, 0x00 };
            static const UCHAR defaultFill = 0x32; // '2'
            RtlFillMemory(DeviceExtension->ScratchContext.ScratchBuffer, 0x0054, defaultFill);
            RtlCopyMemory(DeviceExtension->ScratchContext.ScratchBuffer, results, SIZEOF_ARRAY(results));
            status = STATUS_SUCCESS;
#endif
        if (NT_SUCCESS(status))
        {
            PDVD_DESCRIPTOR_HEADER  header = DeviceExtension->ScratchContext.ScratchBuffer;
            ULONG                   dataLengthToCopy = sizeof(AACS_CHALLENGE_KEY);

            // make length field native byte ordering
            REVERSE_SHORT(&header->Length);

            // exit if getting invalid data from the drive
            if (header->Length < sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length))
            {
                *DataLength = 0;
                status = STATUS_IO_DEVICE_ERROR;
            }

            if (NT_SUCCESS(status))
            {
                // adjust data length to reflect only the addition data
                header->Length -= sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length);

                // exit if the drive is returning an unexpected data size
                if (header->Length != dataLengthToCopy)
                {
                    *DataLength = 0;
                    status = STATUS_IO_DEVICE_ERROR;
                }
            }

            if (NT_SUCCESS(status))
            {
                // else copy the data to the user's buffer
                RtlCopyMemory(outputBuffer, header->Data, dataLengthToCopy);
                *DataLength = dataLengthToCopy;
            }
        }

        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleSendChallengeKey(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
    _In_  WDFREQUEST               Request, 
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:
    This routine is used to process IOCTL:
        IOCTL_AACS_SEND_CHALLENGE_KEY
Arguments:
    DeviceExtension - device context

    Request - the request that will be formatted

    RequestParameters - request parameter structur

    DataLength - data transferred length

Return Value:
    NTSTATUS

  --*/
{
    //AacsSendChallengeKey

    NTSTATUS                 status = STATUS_SUCCESS;
    PAACS_SEND_CHALLENGE_KEY input = NULL;

    PAGED_CODE();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           (PVOID*)&input,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        ULONG   dataTransferLength = sizeof(CDVD_KEY_HEADER) + sizeof(AACS_CHALLENGE_KEY);
        CDB     cdb;

        ScratchBuffer_BeginUse(DeviceExtension);

        // copy the input buffer to the data buffer for the transfer
        {
            PCDVD_KEY_HEADER header = DeviceExtension->ScratchContext.ScratchBuffer;
            ULONG tmp = dataTransferLength;
            tmp -= RTL_SIZEOF_THROUGH_FIELD(CDVD_KEY_HEADER, DataLength);

            header->DataLength[0] = (UCHAR)(tmp >> (8*1));
            header->DataLength[1] = (UCHAR)(tmp >> (8*0));
            RtlCopyMemory(header->Data, &(input->ChallengeKey), sizeof(AACS_CHALLENGE_KEY));
        }

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        cdb.SEND_KEY.OperationCode = SCSIOP_SEND_KEY;
        cdb.AsByte[7] = 0x02; // AACS key class
        cdb.SEND_KEY.ParameterListLength[0] = (UCHAR)(dataTransferLength >> (8*1));
        cdb.SEND_KEY.ParameterListLength[1] = (UCHAR)(dataTransferLength >> (8*0));
        cdb.SEND_KEY.AGID = (UCHAR)( input->SessionId );
        cdb.SEND_KEY.KeyFormat = 0x02; // Send Host Challenge Certificate

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, dataTransferLength, FALSE, &cdb, 12);

#ifdef ENABLE_AACS_TESTING
            status = STATUS_SUCCESS;
#endif
        if (NT_SUCCESS(status))
        {
            *DataLength = 0;
        }

        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleReadVolumeId(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
    _In_  WDFREQUEST               Request, 
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:
    This routine is used to process IOCTL:
        IOCTL_AACS_READ_VOLUME_ID
Arguments:
    DeviceExtension - device context

    Request - the request that will be formatted

    RequestParameters - request parameter structur

    DataLength - data transferred length

Return Value:
    NTSTATUS

  --*/
{
    //AacsReadVolumeID

    NTSTATUS        status = STATUS_SUCCESS;
    PDVD_SESSION_ID input = NULL;
    PVOID           outputBuffer = NULL;

    PAGED_CODE();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           (PVOID*)&input,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                (PVOID*)&outputBuffer,
                                                NULL);
    }

    if (NT_SUCCESS(status))
    {
        ULONG   dataTransferLength = sizeof(CDVD_KEY_HEADER) + sizeof(AACS_VOLUME_ID);
        CDB     cdb;

        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        cdb.READ_DVD_STRUCTURE.OperationCode = SCSIOP_READ_DVD_STRUCTURE;
        cdb.READ_DVD_STRUCTURE.Format = 0x80; // Return the AACS volumeID
        cdb.READ_DVD_STRUCTURE.AllocationLength[0] = (UCHAR)(dataTransferLength >> (8*1));
        cdb.READ_DVD_STRUCTURE.AllocationLength[1] = (UCHAR)(dataTransferLength >> (8*0));
        cdb.READ_DVD_STRUCTURE.AGID = (UCHAR)(*input);

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, dataTransferLength, TRUE, &cdb, 12);

#ifdef ENABLE_AACS_TESTING
            static const UCHAR results[] = { 0x00, 0x22, 0x00, 0x00 };
            static const UCHAR defaultFill = 0x33; // '3'
            RtlFillMemory(DeviceExtension->ScratchContext.ScratchBuffer, 0x0024, defaultFill);
            RtlCopyMemory(DeviceExtension->ScratchContext.ScratchBuffer, results, SIZEOF_ARRAY(results));
            status = STATUS_SUCCESS;
#endif
        if (NT_SUCCESS(status))
        {
            PDVD_DESCRIPTOR_HEADER  header = DeviceExtension->ScratchContext.ScratchBuffer;
            ULONG                   dataLengthToCopy = sizeof(AACS_VOLUME_ID);

            // make length field native byte ordering
            REVERSE_SHORT(&header->Length);

            // exit if getting invalid data from the drive
            if (header->Length < sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length))
            {
                *DataLength = 0;
                status = STATUS_IO_DEVICE_ERROR;
            }

            if (NT_SUCCESS(status))
            {
                // adjust data length to reflect only the addition data
                header->Length -= sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length);

                // exit if the drive is returning an unexpected data size
                if (header->Length != dataLengthToCopy)
                {
                    *DataLength = 0;
                    status = STATUS_IO_DEVICE_ERROR;
                }
            }

            if (NT_SUCCESS(status))
            {
                // else copy the data to the user's buffer
                RtlCopyMemory(outputBuffer, header->Data, dataLengthToCopy);
                *DataLength = dataLengthToCopy;
            }
        }

        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleAacsReadSerialNumber(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
    _In_  WDFREQUEST               Request, 
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:
    This routine is used to process IOCTL:
        IOCTL_AACS_READ_SERIAL_NUMBER
Arguments:
    DeviceExtension - device context

    Request - the request that will be formatted

    RequestParameters - request parameter structur

    DataLength - data transferred length

Return Value:
    NTSTATUS

  --*/
{
    //AacsReadSerialNumber

    NTSTATUS        status = STATUS_SUCCESS;
    PDVD_SESSION_ID input = NULL;
    PVOID           outputBuffer = NULL;

    PAGED_CODE();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           (PVOID*)&input,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                (PVOID*)&outputBuffer,
                                                NULL);
    }

    if (NT_SUCCESS(status))
    {
        ULONG   dataTransferLength = sizeof(CDVD_KEY_HEADER) + sizeof(AACS_SERIAL_NUMBER);
        CDB     cdb;

        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        cdb.READ_DVD_STRUCTURE.OperationCode = SCSIOP_READ_DVD_STRUCTURE;
        cdb.READ_DVD_STRUCTURE.Format = 0x81; // Return the AACS volumeID
        cdb.READ_DVD_STRUCTURE.AllocationLength[0] = (UCHAR)(dataTransferLength >> (8*1));
        cdb.READ_DVD_STRUCTURE.AllocationLength[1] = (UCHAR)(dataTransferLength >> (8*0));
        cdb.READ_DVD_STRUCTURE.AGID = (UCHAR)(*input);

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, dataTransferLength, TRUE, &cdb, 12);

#ifdef ENABLE_AACS_TESTING
            static const UCHAR results[] = { 0x00, 0x22, 0x00, 0x00 };
            static const UCHAR defaultFill = 0x34; // '4'
            RtlFillMemory(DeviceExtension->ScratchContext.ScratchBuffer, 0x0024, defaultFill);
            RtlCopyMemory(DeviceExtension->ScratchContext.ScratchBuffer, results, SIZEOF_ARRAY(results));
            status = STATUS_SUCCESS;
#endif
        if (NT_SUCCESS(status))
        {
            PDVD_DESCRIPTOR_HEADER  header = DeviceExtension->ScratchContext.ScratchBuffer;
            ULONG                   dataLengthToCopy = sizeof(AACS_SERIAL_NUMBER);

            // make length field native byte ordering
            REVERSE_SHORT(&header->Length);

            // exit if getting invalid data from the drive
            if (header->Length < sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length))
            {
                *DataLength = 0;
                status = STATUS_IO_DEVICE_ERROR;
            }

            if (NT_SUCCESS(status))
            {
                // adjust data length to reflect only the addition data
                header->Length -= sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length);

                // exit if the drive is returning an unexpected data size
                if (header->Length != dataLengthToCopy)
                {
                    *DataLength = 0;
                    status = STATUS_IO_DEVICE_ERROR;
                }
            }

            if (NT_SUCCESS(status))
            {
                // else copy the data to the user's buffer
                RtlCopyMemory(outputBuffer, header->Data, dataLengthToCopy);
                *DataLength = dataLengthToCopy;
            }
        }

        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleAacsReadMediaId(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
    _In_  WDFREQUEST               Request, 
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:
    This routine is used to process IOCTL:
        IOCTL_AACS_READ_MEDIA_ID
Arguments:
    DeviceExtension - device context

    Request - the request that will be formatted

    RequestParameters - request parameter structur

    DataLength - data transferred length

Return Value:
    NTSTATUS

  --*/
{
    //AacsReadMediaID

    NTSTATUS        status = STATUS_SUCCESS;
    PDVD_SESSION_ID input = NULL;
    PVOID           outputBuffer = NULL;

    PAGED_CODE();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           (PVOID*)&input,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                (PVOID*)&outputBuffer,
                                                NULL);
    }

    if (NT_SUCCESS(status))
    {
        ULONG   dataTransferLength = sizeof(CDVD_KEY_HEADER) + sizeof(AACS_MEDIA_ID);
        CDB     cdb;

        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        cdb.READ_DVD_STRUCTURE.OperationCode = SCSIOP_READ_DVD_STRUCTURE;
        cdb.READ_DVD_STRUCTURE.Format = 0x82; // Return the AACS volumeID
        cdb.READ_DVD_STRUCTURE.AllocationLength[0] = (UCHAR)(dataTransferLength >> (8*1));
        cdb.READ_DVD_STRUCTURE.AllocationLength[1] = (UCHAR)(dataTransferLength >> (8*0));
        cdb.READ_DVD_STRUCTURE.AGID = (UCHAR)(*input);

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, dataTransferLength, TRUE, &cdb, 12);

#ifdef ENABLE_AACS_TESTING
            static const UCHAR results[] = { 0x00, 0x22, 0x00, 0x00 };
            static const UCHAR defaultFill = 0x35; // '5'
            RtlFillMemory(DeviceExtension->ScratchContext.ScratchBuffer, 0x0024, defaultFill);
            RtlCopyMemory(DeviceExtension->ScratchContext.ScratchBuffer, results, SIZEOF_ARRAY(results));
            status = STATUS_SUCCESS;
#endif
        if (NT_SUCCESS(status))
        {
            PDVD_DESCRIPTOR_HEADER  header = DeviceExtension->ScratchContext.ScratchBuffer;
            ULONG                   dataLengthToCopy = sizeof(AACS_MEDIA_ID);

            // make length field native byte ordering
            REVERSE_SHORT(&header->Length);

            // exit if getting invalid data from the drive
            if (header->Length < sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length))
            {
                *DataLength = 0;
                status = STATUS_IO_DEVICE_ERROR;
            }

            if (NT_SUCCESS(status))
            {
                // adjust data length to reflect only the addition data
                header->Length -= sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length);

                // exit if the drive is returning an unexpected data size
                if (header->Length != dataLengthToCopy)
                {
                    *DataLength = 0;
                    status = STATUS_IO_DEVICE_ERROR;
                }
            }

            if (NT_SUCCESS(status))
            {
                // else copy the data to the user's buffer
                RtlCopyMemory(outputBuffer, header->Data, dataLengthToCopy);
                *DataLength = dataLengthToCopy;
            }
        }

        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleAacsReadBindingNonce(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
    _In_  WDFREQUEST               Request, 
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:
    This routine is used to process IOCTL:
        IOCTL_AACS_READ_BINDING_NONCE
Arguments:
    DeviceExtension - device context

    Request - the request that will be formatted

    RequestParameters - request parameter structur

    DataLength - data transferred length

Return Value:
    NTSTATUS

  --*/
{
    //AacsReadBindingNonce

    NTSTATUS                 status = STATUS_SUCCESS;
    PAACS_READ_BINDING_NONCE input = NULL;
    PVOID                    outputBuffer = NULL;

    PAGED_CODE();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           (PVOID*)&input,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                (PVOID*)&outputBuffer,
                                                NULL);
    }

    if (NT_SUCCESS(status))
    {
        ULONG   dataTransferLength = sizeof(CDVD_KEY_HEADER) + sizeof(AACS_BINDING_NONCE);
        CDB     cdb;

        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB
        cdb.REPORT_KEY.OperationCode = SCSIOP_REPORT_KEY;
        cdb.REPORT_KEY.LogicalBlockAddress[0] = (UCHAR)( input->StartLba >> (3*8) );
        cdb.REPORT_KEY.LogicalBlockAddress[1] = (UCHAR)( input->StartLba >> (2*8) );
        cdb.REPORT_KEY.LogicalBlockAddress[2] = (UCHAR)( input->StartLba >> (1*8) );
        cdb.REPORT_KEY.LogicalBlockAddress[3] = (UCHAR)( input->StartLba >> (0*8) );
        cdb.AsByte[6] = (UCHAR)( input->NumberOfSectors );
        cdb.AsByte[7] = 0x02; // AACS key class
        cdb.REPORT_KEY.AllocationLength[0] = (UCHAR)(dataTransferLength >> (8*1));
        cdb.REPORT_KEY.AllocationLength[1] = (UCHAR)(dataTransferLength >> (8*0));
        cdb.REPORT_KEY.AGID = (UCHAR)( input->SessionId );
        cdb.REPORT_KEY.KeyFormat = 0x21; // Return an existing binding nonce

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, dataTransferLength, TRUE, &cdb, 12);

#ifdef ENABLE_AACS_TESTING
            static const UCHAR results[] = { 0x00, 0x22, 0x00, 0x00 };
            static const UCHAR defaultFill = 0x36; // '6'
            RtlFillMemory(DeviceExtension->ScratchContext.ScratchBuffer, 0x0024, defaultFill);
            RtlCopyMemory(DeviceExtension->ScratchContext.ScratchBuffer, results, SIZEOF_ARRAY(results));
            status = STATUS_SUCCESS;
#endif
        if (NT_SUCCESS(status))
        {
            PDVD_DESCRIPTOR_HEADER  header = DeviceExtension->ScratchContext.ScratchBuffer;
            ULONG                   dataLengthToCopy = sizeof(AACS_BINDING_NONCE);

            // make length field native byte ordering
            REVERSE_SHORT(&header->Length);

            // exit if getting invalid data from the drive
            if (header->Length < sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length))
            {
                *DataLength = 0;
                status = STATUS_IO_DEVICE_ERROR;
            }

            if (NT_SUCCESS(status))
            {
                // adjust data length to reflect only the addition data
                header->Length -= sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length);

                // exit if the drive is returning an unexpected data size
                if (header->Length != dataLengthToCopy)
                {
                    *DataLength = 0;
                    status = STATUS_IO_DEVICE_ERROR;
                }
            }

            if (NT_SUCCESS(status))
            {
                // else copy the data to the user's buffer
                RtlCopyMemory(outputBuffer, header->Data, dataLengthToCopy);
                *DataLength = dataLengthToCopy;
            }
        }

        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

_IRQL_requires_max_(APC_LEVEL)
NTSTATUS
DeviceHandleAacsGenerateBindingNonce(
    _In_  PCDROM_DEVICE_EXTENSION  DeviceExtension, 
    _In_  WDFREQUEST               Request, 
    _In_  WDF_REQUEST_PARAMETERS   RequestParameters,
    _Out_ size_t *                 DataLength
    )
/*++

Routine Description:
    This routine is used to process IOCTL:
        IOCTL_AACS_GENERATE_BINDING_NONCE
Arguments:
    DeviceExtension - device context

    Request - the request that will be formatted

    RequestParameters - request parameter structur

    DataLength - data transferred length

Return Value:
    NTSTATUS

  --*/
{
    //AacsGenerateBindingNonce

    NTSTATUS                 status = STATUS_SUCCESS;
    PAACS_READ_BINDING_NONCE input = NULL;
    PVOID                    outputBuffer = NULL;

    PAGED_CODE();

    *DataLength = 0;

    status = WdfRequestRetrieveInputBuffer(Request,
                                           RequestParameters.Parameters.DeviceIoControl.InputBufferLength,
                                           (PVOID*)&input,
                                           NULL);

    if (NT_SUCCESS(status))
    {
        status = WdfRequestRetrieveOutputBuffer(Request,
                                                RequestParameters.Parameters.DeviceIoControl.OutputBufferLength,
                                                (PVOID*)&outputBuffer,
                                                NULL);
    }

    if (NT_SUCCESS(status))
    {
        ULONG   dataTransferLength = sizeof(CDVD_KEY_HEADER) + sizeof(AACS_BINDING_NONCE);
        CDB     cdb;

        ScratchBuffer_BeginUse(DeviceExtension);

        RtlZeroMemory(&cdb, sizeof(CDB));
        // Set up the CDB

        status = ScratchBuffer_ExecuteCdb(DeviceExtension, Request, dataTransferLength, TRUE, &cdb, 12);

#ifdef ENABLE_AACS_TESTING
            static const UCHAR results[] = { 0x00, 0x22, 0x00, 0x00 };
            static const UCHAR defaultFill = 0x37; // '7'
            RtlFillMemory(DeviceExtension->ScratchContext.ScratchBuffer, 0x0024, defaultFill);
            RtlCopyMemory(DeviceExtension->ScratchContext.ScratchBuffer, results, SIZEOF_ARRAY(results));
            status = STATUS_SUCCESS;
#endif
        if (NT_SUCCESS(status))
        {
            PDVD_DESCRIPTOR_HEADER  header = DeviceExtension->ScratchContext.ScratchBuffer;
            ULONG                   dataLengthToCopy = sizeof(AACS_BINDING_NONCE);

            // make length field native byte ordering
            REVERSE_SHORT(&header->Length);

            // exit if getting invalid data from the drive
            if (header->Length < sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length))
            {
                *DataLength = 0;
                status = STATUS_IO_DEVICE_ERROR;
            }

            if (NT_SUCCESS(status))
            {
                // adjust data length to reflect only the addition data
                header->Length -= sizeof(DVD_DESCRIPTOR_HEADER) - RTL_SIZEOF_THROUGH_FIELD(DVD_DESCRIPTOR_HEADER, Length);

                // exit if the drive is returning an unexpected data size
                if (header->Length != dataLengthToCopy)
                {
                    *DataLength = 0;
                    status = STATUS_IO_DEVICE_ERROR;
                }
            }

            if (NT_SUCCESS(status))
            {
                // else copy the data to the user's buffer
                RtlCopyMemory(outputBuffer, header->Data, dataLengthToCopy);
                *DataLength = dataLengthToCopy;
            }
        }

        ScratchBuffer_EndUse(DeviceExtension);
    }

    return status;
}

Our Services

  • What our customers say about us?

© 2011-2024 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS