protected void DispatchCommand()

in TSS.Java/src/tss/TpmBase.java [221:445]


    protected void DispatchCommand(TPM_CC cmdCode, ReqStructure req, RespStructure resp)
    { try {
        TPM_HANDLE[] inHandles = req.getHandles();
        int numAuthHandles = req.numAuthHandles();

        boolean hasSessions = numAuthHandles != 0 || Sessions != null;
        int sessTag = hasSessions ? TPM_ST.SESSIONS.toInt() : TPM_ST.NO_SESSIONS.toInt();
        
        TpmBuffer cmdBuf = new TpmBuffer();

        // Standard TPM command header {tag, length, commandCode}
        cmdBuf.writeShort(sessTag);
        cmdBuf.writeInt(0);        // to be filled in later
        cmdBuf.writeInt(cmdCode.toInt());

        // Handles
        int numHandles = inHandles == null ? 0 : inHandles.length;
        for (int i=0; i < numHandles; i++)
            inHandles[i].toTpm(cmdBuf);
        
        // Marshal command params (without handles) to paramBuf
        TpmBuffer paramBuf = new TpmBuffer();
        req.toTpm(paramBuf);
        paramBuf.trim();

        byte[] cpHashData = null;
        
        //
        // Authorization sessions
        //
        if (hasSessions)
        {
            // We do not know the size of the authorization area yet.
            // Remember the place to marshal it, ...
            int authSizePos = cmdBuf.curPos();
            // ... and marshal a placeholder 0 value for now.
            cmdBuf.writeInt(0);

            // todo: Make Sessions type Session[]
            // If not all required sessions were provided explicitly, TSS.Java will create the necessary
            // number of password sessions with auth values (if any) from the corresponding TPM_HANDLE objects.
            int numExplicitSessions = 0;
            if (Sessions == null)
                Sessions = new TPM_HANDLE[numAuthHandles];
            else
            {
                numExplicitSessions = Sessions.length;
                if (numExplicitSessions < numAuthHandles)
                    Sessions = Arrays.copyOf(Sessions, numAuthHandles);
            }
            for (int i = numExplicitSessions; i < numAuthHandles; ++i)
                Sessions[i] = TPM_HANDLE.PW;

            TPMA_SESSION sessAttrs = TPMA_SESSION.continueSession;
            for (int i=0; i < Sessions.length; i++)
            {
                // todo: Add support for policyc sessions with HMAC
                boolean needAuth = i < numHandles && Sessions[i].getType() != TPM_HT.POLICY_SESSION;
                WriteSession (cmdBuf, Sessions[i], null, sessAttrs,
                              needAuth ? inHandles[i].AuthValue : null);
            }
            Sessions = null;

            cmdBuf.writeNumAtPos(cmdBuf.curPos() - authSizePos - 4, authSizePos);
        }
        
        // Write marshaled command params to the command buffer
        cmdBuf.writeByteBuf(paramBuf.buffer());
        
        // Finally, set the command buffer size
        cmdBuf.writeNumAtPos(cmdBuf.curPos(), 2);
        

        if (CpHash != null || AuditCommand)
        {
            if (cpHashData == null)
                cpHashData = GetCpHashData(cmdCode, paramBuf.buffer());
            if (CpHash != null)
            {
                CpHash.digest = Crypto.hash(CpHash.hashAlg, cpHashData);
                clearInvocationState();
                Sessions = null;
                CpHash = null;
                return;
            }
            AuditCpHash.digest = Crypto.hash(CommandAuditHash.hashAlg, cpHashData);
        }



        byte[] rawCmdBuf = cmdBuf.trim();
        int nvRateRecoveryCount = 4;    
        TpmBuffer respBuf = null;
        TPM_ST respTag = TPM_ST.NULL; 
        int respSize = 0;
        int rawResponseCode = 0;

        while (true)
        {
            device.dispatchCommand(rawCmdBuf);
            
            byte[] rawRespBuf = device.getResponse();
            respBuf = new TpmBuffer(rawRespBuf);
            
            // get the standard header
            respTag = TPM_ST.fromTpm(respBuf);
            respSize = respBuf.readInt();
            rawResponseCode = respBuf.readInt();
    
            int actRespSize = respBuf.size();
            if (respSize != actRespSize)
            {
                throw new TpmException(String.format(
                            "Inconsistent TPM response buffer: %d B reported, %d B received", respSize, actRespSize));
            }

            lastResponseCode = TpmHelpers.fromRawResponse(rawResponseCode);
            if (callbackObject != null)
                callbackObject.commandCompleteCallback(cmdCode, lastResponseCode, rawCmdBuf, rawRespBuf);

            if (lastResponseCode == TPM_RC.RETRY)
                continue;

            if (lastResponseCode != TPM_RC.NV_RATE || ++nvRateRecoveryCount > 4)
                break;

            // todo: Enable TPM property retrieval and sleep below, and remove the following break
            break;
            //System.out.println(">>>> NV_RATE: Retrying... Attempt " + nvRateRecoveryCount.toString());
            //Thread.Sleep((int)Tpm2.GetProperty(this, Pt.NvWriteRecovery) + 100);
        }

        // Interpretation of the response code depends on whether the programmer
        // has indicated that an error is expected or allowed.
        if (lastResponseCode != TPM_RC.SUCCESS)
        {
            // error - decode it
            if (AllowErrors)
                return; // Any error is allowed

            if (Helpers.isOneOf(lastResponseCode, ExpectedResponses))
                return; // The given error is expected

            if (_isSuccessExpected())
            {
                System.out.println("TPM ERROR: " + lastResponseCode);
                throw new TpmException(lastResponseCode, rawResponseCode);
            }

            String expected = ExpectedResponses.length > 1 ? Arrays.toString(ExpectedResponses)
                                                           : ExpectedResponses[0].toString();
            throw new TpmException("Unexpected response {" + lastResponseCode + "} instead of {" + expected + "}",
                                   lastResponseCode);
        }
        else if (ExpectedResponses != null)
        {
            String expected = ExpectedResponses.length > 1 ? "s " + Arrays.toString(ExpectedResponses) + " were"
                                                           : " " + ExpectedResponses.toString() + " was";
            throw new TpmException("Error" + expected + " expected, " +
                                   "but the TPM command " + cmdCode + " succeeded"); 
        }

        // Keep a copy of this before we clean out invocation state
        boolean auditCommand = AuditCommand;

        clearInvocationState();

        // A check for the session tag consistency across the command invocation
        // only makes sense when the command succeeds.
        if (respTag.toInt() != sessTag)
            throw new TpmException("Unexpected response tag " + respTag);

        if (resp == null)
            resp = new RespStructure();  // use a placeholder to avoid null checks

        if (resp.numHandles() > 0)
        {
            assert(resp.numHandles() == 1);
            resp.setHandle(TPM_HANDLE.fromTpm(respBuf));
        }

        boolean rpReady = false;
        int     respParamsPos = 0,
                respParamsSize = 0;

        // If there are no sessions then response parameters take up the remaining part
        // of the response buffer. Otherwise the response parameters area is preceded with
        // its size, and followed by the session area.
        if (respTag == TPM_ST.SESSIONS)
        {
            respParamsSize = respBuf.readInt();
            respParamsPos = respBuf.curPos();
            rpReady = processRespSessions(respBuf, cmdCode, respParamsPos, respParamsSize);
        }
        else
        {
            respParamsPos = respBuf.curPos();
            respParamsSize = respBuf.size() - respParamsPos;
        }

        if (auditCommand)
        {
            byte[] rpHash = getRpHash(CommandAuditHash.hashAlg, respBuf,
                                      cmdCode, respParamsPos, respParamsSize, rpReady);
            CommandAuditHash.extend(Helpers.concatenate(AuditCpHash.digest, rpHash));
        }

        // Now we can decrypt (if necessary) the first response parameter
        doParmEncryption(resp, respBuf, respParamsPos, false);

        // ... and unmarshall the whole response parameters area
        respBuf.curPos(respParamsPos);
        resp.initFromTpm(respBuf);
        if (respBuf.curPos() != respParamsPos + respParamsSize)
            throw new TpmException("Bad response parameters area");

        // If there is a returned handle get a pointer to it. It is always the 
        // first element in the structure.
        updateRespHandle(cmdCode, resp);

        Sessions = null;

    } finally {
        clearInvocationState();
    }} // DispatchCommand()