#include "stdafx.h"
#include "SSLEngine.h"

// 関数テーブル
PSecurityFunctionTable    SSLEngine::pFunctions;



/**
 * InitializeSecurityContext()/AcceptSecurityContext()の引数
 */
#define ISC_ATTRIBUTES  (ISC_REQ_SEQUENCE_DETECT   | \
                         ISC_REQ_REPLAY_DETECT     | \
                         ISC_REQ_CONFIDENTIALITY   | \
                         ISC_REQ_EXTENDED_ERROR    | \
                         ISC_REQ_ALLOCATE_MEMORY   | \
                         ISC_REQ_STREAM)
#define ASC_ATTRIBUTES  (ASC_REQ_SEQUENCE_DETECT | \
                         ASC_REQ_REPLAY_DETECT   | \
                         ASC_REQ_CONFIDENTIALITY | \
                         ASC_REQ_EXTENDED_ERROR  | \
                         ASC_REQ_ALLOCATE_MEMORY | \
                         ASC_REQ_STREAM)

static LPSTR g_server_cert_usage[] =
{
  szOID_PKIX_KP_SERVER_AUTH,
  szOID_SERVER_GATED_CRYPTO,
  szOID_SGC_NETSCAPE
};

static LPSTR g_client_cert_usage[] =
{
  szOID_PKIX_KP_CLIENT_AUTH,
};


#ifdef DEBUG
static void
hexDump(const _TCHAR* header, unsigned char* buffer, DWORD length)
{
  static const _TCHAR num_chars[]=_T("0123456789ABCDEF");

  OutputDebugString(header);
  OutputDebugString(_T("\r\n"));

  DWORD pos = 0;
  while (length > 0)
  {
    _TCHAR line[128];
    _stprintf(line, _T("%4.4x: "), pos);
    _TCHAR* work = line + 6;
    
    DWORD count = (length > 16) ? 16 : length;
    DWORD i;
    for (i = 0; i < count; i++)
    {
      *work++ = num_chars[buffer[i] >> 4];
      *work++ = num_chars[buffer[i] & 0x0f];
      if(i == 7)
      {
        *work++ = ':';
      } 
      else
      {
        *work++ = ' ';
      }
    }
    while (i < 16)
    {
      *work++ = ' ';
      *work++ = ' ';
      *work++ = ' ';
      i++;
    }

    *work++ = ' ';

    for (i = 0; i < count; i++)
    {
      if (buffer[i] < 32 || buffer[i] > 126) 
      {
        *work++ = '.';
      } 
      else
      {
        *work++ = buffer[i];
      }
    }
          
    *work++ = _T('\0');

    OutputDebugString(line);
    OutputDebugString(_T("\r\n"));
    
    pos += count;
    buffer += count; 
    length -= count;
  }
}

#define DUMP(t,b,l) hexDump((t),(unsigned char*)(b),(l))
#define DBG(s) OutputDebugString(s)
#else

#define DUMP(t,b,l)
#define DBG(s)

#endif

/**
 * 証明書チェーンを得る
 */
static BOOL getCertificateChain(PCCERT_CONTEXT cert, PCCERT_CHAIN_CONTEXT* ppChain, LPSTR* usage, DWORD usage_count)
{
    CERT_CHAIN_PARA chain_para = { sizeof(CERT_CHAIN_PARA) };
    chain_para.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
    chain_para.RequestedUsage.Usage.rgpszUsageIdentifier = usage;
    chain_para.RequestedUsage.Usage.cUsageIdentifier = usage_count;
    return CertGetCertificateChain(NULL,             // hChainEngine
                                   cert,             // pCertContext
                                   NULL,             // pTime
                                   cert->hCertStore, // hAdditionalStore
                                   &chain_para,      // pChainPara
                                   0,                // dwFlags
                                   NULL,             // pvReserved
                                   ppChain);         // ppChainContext
}

SSLEngine::SSLEngine(const _TCHAR* host)
          : 
            peerHost(NULL),
            clientMode(true),
            outboundDone(false),
            inboundDone(false),
            phCredential(NULL),
            phContext(NULL),
            handshakeStatus(NOT_HANDSHAKING),
            lastHandshakeTrigger(HELLO_REQUEST),
            enabledProtocols(SP_PROT_SSL3|SP_PROT_TLS1),
            pLocalCertificate(NULL),
            pLocalCertificateChain(NULL),
            pPeerCertificate(NULL),
            pPeerCertificateChain(NULL),
            protocolVersion(0x0000),
            sessionIDLength(0),
            sessionID(NULL),
            cipherSuiteCode(0x0000),
            delegatedTaskCount(0),
            peerCertificateChainLoaded(false),
            clientAuthLevel(0)
{
  // ロックを作成する
  InitializeCriticalSection(&this->criticalSection);

  if (host)
  {
    size_t len = _tcslen(host);
    _TCHAR* tmp = new _TCHAR[len + 1];
    _tcscpy(tmp, host);
    this->peerHost = tmp;
  }

  // 関数テーブルを初期化する
  if (! pFunctions)
  {
    pFunctions = InitSecurityInterface();
  }

  memset(this->securityContextOutput, 0, sizeof(this->securityContextOutput));            
  memset(&this->streamSizes, 0, sizeof(this->streamSizes));
}

SSLEngine::~SSLEngine()
{
  DeleteCriticalSection(&this->criticalSection);

  _TCHAR* tmp = const_cast<_TCHAR*>(this->peerHost);
  this->peerHost = NULL;
  delete tmp;

  // 出力バッファを削除する
  if (this->securityContextOutput[0].pvBuffer)
  {
    FreeContextBuffer(this->securityContextOutput[0].pvBuffer);
    this->securityContextOutput[0].pvBuffer = NULL;
  }

  // 証明書チェーンを削除する
  if (this->pPeerCertificateChain)
  {
    CertFreeCertificateChain(this->pPeerCertificateChain);
    this->pPeerCertificateChain = NULL;
  }

  // 証明書を削除する
  if (this->pPeerCertificate)
  {
    CertFreeCertificateContext(this->pPeerCertificate);
    this->pPeerCertificate = NULL;
  }

  // ローカル証明書チェーンを削除する
  if (this->pLocalCertificateChain)
  {
    CertFreeCertificateChain(this->pLocalCertificateChain);
    this->pLocalCertificateChain = NULL;
  }

  if (this->pLocalCertificate)
  {
    // CertDuplicateCertificateContext()を呼び出していないので、
    // 解放も不要
//    CertFreeCertificateContext(this->pLocalCertificate);
    this->pLocalCertificate = NULL;
  }

  // CtxtHandleを削除する
  if (this->phContext)
  {
    SECURITY_STATUS ss = DeleteSecurityContext(this->phContext);
    delete this->phContext;
    this->phContext = NULL;
  }

  // CredHandleを削除する
  if (this->phCredential)
  {
    SECURITY_STATUS ss = FreeCredentialsHandle(this->phCredential);
    delete this->phCredential;
    this->phCredential = NULL;
  }

  if (this->sessionID)
  {
    delete this->sessionID;
    this->sessionID = NULL;
  }

  // ロックを開放する
  DeleteCriticalSection(&this->criticalSection);
}

/**
 * 相手側の証明書チェーンを取得する
 */
bool
SSLEngine::loadPeerCertificateChain()
{
  bool result = false;

  if (this->pPeerCertificate)
  {
    // 証明書チェーンを得る
    // クライアントモードの場合は、サーバ証明書を認証する
    // サーバモードの場合は、クライアント証明書を認証する
    LPSTR* usage = getUseClientMode() ? g_server_cert_usage : g_client_cert_usage;
    const DWORD usage_count = getUseClientMode() ? (sizeof(g_server_cert_usage) / sizeof(LPSTR))
                                                 : (sizeof(g_client_cert_usage) / sizeof(LPSTR));

    if (! getCertificateChain(this->pPeerCertificate,
                              &this->pPeerCertificateChain,
                              usage,
                              usage_count))
    {
      // 証明書チェーン取得処理自体が失敗
      // FreeContextBuffer(this->pPeerCertificate);
      // this->pPeerCertificate = NULL;
      goto END;
    }

    // 証明書検証結果は、SSLEngine.getSession()が呼び出された時点で確認する
  }

  result = true;

END:
  this->peerCertificateChainLoaded = true;
  return result;
}

/**
 * 自局の証明書チェーンを返す
 */
PCCERT_CHAIN_CONTEXT
SSLEngine::getLocalCertificateChain()
{
  if (this->pLocalCertificate && (! this->pLocalCertificateChain))
  {
    // 証明書チェーンを得る
    // クライアントモードの場合は、用途に「クライアント証明書」を
    // サーバモードの場合は、用途に「サーバ証明書」を
    // それぞれ指定する
    LPSTR* usage = getUseClientMode() ? g_client_cert_usage : g_server_cert_usage;
    const DWORD usage_count = getUseClientMode() ? (sizeof(g_client_cert_usage) / sizeof(LPSTR))
                                                 : (sizeof(g_server_cert_usage) / sizeof(LPSTR));

    getCertificateChain(this->pLocalCertificate,
                        &this->pLocalCertificateChain,
                        usage,
                        usage_count);
  }
  return this->pLocalCertificateChain;
}

void
SSLEngine::encryptApplicationData(void* srcbuff, int srclen, void* destbuff, int destlen)
{
  assert(this->phContext);
  assert(this->streamSizes.cBuffers >= 4);

  // http://msdn.microsoft.com/en-us/library/aa925742.aspx
  // Buffer Type   	    Description
  // SECBUFFER_STREAM_HEADER  Used internally. No initialization required.
  // SECBUFFER_DATA           Contains the plaintext message to be encrypted.
  // SECBUFFER_STREAM_TRAILER Used internally. No initialization required.
  // SECBUFFER_EMPTY          Used internally. No initialization required. Size can be zero. 

  // 入力データの最大長を計算する
  // http://support.microsoft.com/?scid=kb%3Ben-us%3B262403&x=21&y=16
  // の情報に基づき、
  // cbHeader + cbMaximumMessage + cbTrailer
  // *ではなく*
  // cbMaximumMessage - (cbHeader + cbTrailer)
  // をバッファサイズとしている
  // 上記URLの情報はPocket PCのものではないが、念のためこうしている
  const size_t max_data = this->streamSizes.cbMaximumMessage
                              - (this->streamSizes.cbHeader + this->streamSizes.cbTrailer);

  // 入力データ長を決定する
  const size_t data_len = min((size_t) srclen, max_data);

  if (srclen <= 0)
  {
    // 出力データなし
    this->wrapResult.setValues(OK,
                               getHandshakeStatus(),
                               0,
                               0);
  }
  else if ((size_t) destlen < this->streamSizes.cbHeader + data_len + this->streamSizes.cbTrailer)
  {
    this->wrapResult.setValues(BUFFER_OVERFLOW,
                               getHandshakeStatus(),
                               0,
                               0);
  }
  else
  {
    memcpy((char*) destbuff + this->streamSizes.cbHeader,
           srcbuff,
           data_len);

    SecBuffer buffers[4];

    // header
    buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
    buffers[0].cbBuffer = this->streamSizes.cbHeader;
    buffers[0].pvBuffer = destbuff;

    buffers[1].BufferType = SECBUFFER_DATA;
    buffers[1].cbBuffer = data_len;
    buffers[1].pvBuffer = (char*) destbuff + this->streamSizes.cbHeader;

    // trailer
    buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
    buffers[2].cbBuffer = this->streamSizes.cbTrailer;
    buffers[2].pvBuffer = (char*) destbuff + this->streamSizes.cbHeader + data_len;

    // empty
    buffers[3].BufferType = SECBUFFER_EMPTY;
    buffers[3].cbBuffer = 0;
    buffers[3].pvBuffer = NULL;

    SecBufferDesc msg;
    msg.ulVersion = SECBUFFER_VERSION;
    msg.cBuffers = 4;
    msg.pBuffers = buffers;

    SECURITY_STATUS ss
      = pFunctions->EncryptMessage(
                        this->phContext,  // phContext
                        0,                // fQOP
                        &msg,             // pMessage
                        0                 // MessageSeqNo
                        );
    switch (ss)
    {
    case SEC_E_OK:
      {
        const size_t copy_len = buffers[0].cbBuffer
                                + buffers[1].cbBuffer
                                + buffers[2].cbBuffer;
        if (copy_len > (size_t) destlen)
        {
          // これは発生しない（はず）
          assert(false);
        }
        this->wrapResult.setValues(OK,
                                   getHandshakeStatus(),
                                   data_len,
                                   copy_len);
      }
      break;

    case SEC_I_RENEGOTIATE:
      // ToDo: 再ネゴシエート処理
      assert(false);
      this->wrapResult.setError(ss);
      break;

    default:
      assert(false);
      this->wrapResult.setError(ss);
      break;
    }
  }
}

/**
 * close_notifyアラートを生成する
 */
void
SSLEngine::wrapCloseNotify()
{
  // ApplyControlToken()にSCHANNEL_SHUTDOWNを渡して
  // シャットダウン処理を開始する
  DWORD dwType = SCHANNEL_SHUTDOWN;
  SecBuffer inBuff[1];
  inBuff[0].pvBuffer   = &dwType;
  inBuff[0].BufferType = SECBUFFER_TOKEN;
  inBuff[0].cbBuffer   = sizeof(dwType);

  SecBufferDesc sbdIn;
  sbdIn.cBuffers  = 1;
  sbdIn.pBuffers  = inBuff;
  sbdIn.ulVersion = SECBUFFER_VERSION;

  this->securityStatus = pFunctions->ApplyControlToken(this->phContext, &sbdIn);
  if (FAILED(this->securityStatus)) 
  {
    // シャットダウン処理を開始できない
    assert(false);
    return;
  }

  SecBuffer inBuffer[2];
  inBuffer[0].pvBuffer   = NULL;
  inBuffer[0].cbBuffer   = 0;
  inBuffer[0].BufferType = SECBUFFER_EMPTY;
  inBuffer[1].pvBuffer   = NULL;
  inBuffer[1].cbBuffer   = 0;
  inBuffer[1].BufferType = SECBUFFER_EMPTY;

  sbdIn.ulVersion = SECBUFFER_VERSION;
  sbdIn.cBuffers  = 2;
  sbdIn.pBuffers  = inBuffer;

  this->securityContextOutput[0].cbBuffer   = 0;
  this->securityContextOutput[0].BufferType = SECBUFFER_TOKEN;
  this->securityContextOutput[0].pvBuffer   = NULL;

  SecBufferDesc sbdOut;
  sbdOut.ulVersion = SECBUFFER_VERSION;
  sbdOut.cBuffers  = 1;
  sbdOut.pBuffers  = this->securityContextOutput;

  TimeStamp tsExpiry;
  unsigned long uAttrOut;
  if (getUseClientMode())
  {
    this->securityStatus
      = pFunctions->InitializeSecurityContext(this->phCredential,
                                  this->phContext,
                                  const_cast<SEC_WCHAR*>(getPeerHost()),
                                  ISC_ATTRIBUTES,
                                  0,
                                  SECURITY_NETWORK_DREP,
                                  &sbdIn,
                                  0,
                                  this->phContext,
                                  &sbdOut,
                                  &uAttrOut,
                                  &tsExpiry);
  }
  else
  {
    this->securityStatus
      = pFunctions->AcceptSecurityContext(this->phCredential,
                                this->phContext,
                                &sbdIn,
                                ASC_ATTRIBUTES,
                                SECURITY_NATIVE_DREP,
                                this->phContext,
                                &sbdOut,
                                &uAttrOut,
                                &tsExpiry);
    // テスト用
#ifdef DEBUG
    {
      LONG result = uAttrOut & ASC_RET_MUTUAL_AUTH;
      result = result;
    }
#endif

  }

  if (SEC_E_OK != this->securityStatus)
  {
    // close_notifyアラートの生成失敗
    assert(false);
  }
}

/**
 * 出力をクローズする
 */
void
SSLEngine::closeOutbound()
{
  if (isOutboundDone())
  {
    // すでに出力が閉じている
    return;
  }

  if (! this->phContext || ! this->phCredential)
  {
    // まだ初期化されていない
    return;
  }

  const SSLEngineHandshakeTrigger lht = getLastHandshakeTrigger();
  if (lht == CLOSE_NOTIFY_RECEIVED || lht == OUTBOUND_CLOSED)
  {
    // 出力は生きているが、終了処理中
    return;
  }

  // close_notifyアラートを生成する
  wrapCloseNotify();

  // ハンドシェーク開始原因を設定
  setLastHandshakeTrigger(OUTBOUND_CLOSED);

  // ハンドシェーク状態を更新
  updateHandshakeStatus();

  // ハンドシェークステータスは NEED_WRAP のはず
  assert(NEED_WRAP == this->handshakeStatus);
}

/**
 * 入力をクローズする
 */
bool
SSLEngine::closeInbound()
{
  if (isInboundDone())
  {
    // すでに入力は閉じている
    return true;
  }
  
  // 入力を閉じる
  setInboundDone(true);

  if (! this->phContext || ! this->phCredential)
  {
    // 何もしていないうちに閉じた
    return true;
  }

  // close_alertを送信または受信しているか確認する
  SSLEngineHandshakeTrigger lht = getLastHandshakeTrigger();
  if (lht != CLOSE_NOTIFY_RECEIVED && lht != OUTBOUND_CLOSED)
  {
    // truncate attack を受けている可能性がある
    return false;
  }

  return true;
}

const SSLEngineResult&
SSLEngine::wrap(void* srcbuff, int srclen, void* destbuff, int destlen)
{
  // ロックを取得
  acquireLock();

  if (isOutboundDone())
  {
    // 出力がクローズされている
    this->wrapResult.setValues(CLOSED, getHandshakeStatus(), 0, 0);
    goto END;
  }
 
  SSLEngineHandshakeStatus hs = getHandshakeStatus();
  SSLEngineHandshakeTrigger lht = getLastHandshakeTrigger();

  if (! this->phCredential && HELLO_REQUEST == lht)
  {
    // 初期ハンドシェーク開始
    HRESULT result = beginHandshake();
    if (FAILED(result))
    {
      // ハンドシェーク開始失敗
      this->wrapResult.setError(result);
      goto END;
    }
  }

  hs = getHandshakeStatus();
  lht = getLastHandshakeTrigger();
  if (NEED_WRAP == hs)
  {
    // NEED_WRAP

    if (! this->phContext)
    {
      /// コンテキストの初期化を行う
      assert(getUseClientMode());

      // InitializeSecurityContext()の初回呼び出し
      this->phContext = new CtxtHandle();
      if (! this->phContext)
      {
        this->wrapResult.setError(E_OUTOFMEMORY);
        goto END;
      }
      firstInitializeSecurityContext(destbuff, destlen);
    }
    else if (CLOSE_NOTIFY_RECEIVED == lht)
    {
      // close_notifyに対する返答として、close_notifyを生成する
#if 0
      // http://msdn.microsoft.com/en-us/library/aa923704.aspx
      // に以下の記述がある。
      // When you use the Schannel SSP, the DecryptMessage  function returns SEC_I_CONTEXT_EXPIRED when the message sender has shut down the connection. After receiving this return value, your application must complete the following steps:
      //    1. Call EncryptMessage, passing in an empty input buffer.
      //    2. Send the output buffers from the EncryptMessage call to the remote party (the sender of the decrypted message).
      //    3. Delete the security context by calling the DeleteSecurityContext function. 
      // しかし、この出力方法では、EncryptMessage()はSEC_E_CONTEXT_EXPIREDを返してくるため、
      // ドキュメントの誤りと思われる。
      // AcceptControlToken()/InitializeSecurityContext()で生成するのが正しい
      if ((unsigned int) destlen < (this->streamSizes.cbHeader + this->streamSizes.cbTrailer))
      {
        // 出力バッファサイズが足りない
        this->wrapResult.setValues(BUFFER_UNDERFLOW,
                                   getHandshakeStatus(),
                                   0,
                                   0);
      }
      else
      {
        // サイズ0のバッファを指定して、EncryptMessage()を呼び出す
        SecBuffer buffers[4];
        buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
        buffers[0].cbBuffer = this->streamSizes.cbHeader;
        buffers[0].pvBuffer = destbuff;

        buffers[1].BufferType = SECBUFFER_DATA;
        buffers[1].cbBuffer = 0;  // サイズ0
        buffers[1].pvBuffer = (char*) destbuff + this->streamSizes.cbHeader;

        buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
        buffers[2].cbBuffer = this->streamSizes.cbTrailer;
        buffers[2].pvBuffer = (char*) destbuff + this->streamSizes.cbHeader + 0;

        buffers[3].BufferType = SECBUFFER_EMPTY;
        buffers[3].cbBuffer = 0;
        buffers[3].pvBuffer = NULL;

        SecBufferDesc msg;
        msg.ulVersion = SECBUFFER_VERSION;
        msg.cBuffers = 4;
        msg.pBuffers = buffers;

        this->securityStatus
          = pFunctions->EncryptMessage(
                              this->phContext,  //phContext
                              0,                // fQOP
                              &msg,             // pMessage
                              0                 // MessageSeqNo
                              );

        // 出力データはこれが最後
        setOutboundDone(true);

        // ハンドシェーク状態を更新（エラーは無視）
        updateHandshakeStatus();

        int bytesProduced = 0;
        if (SUCCEEDED(this->securityStatus))
        {
          bytesProduced = buffers[0].cbBuffer + buffers[1].cbBuffer + buffers[2].cbBuffer;
        }
        this->wrapResult.setValues(CLOSED,                // 出力データなし
                                   getHandshakeStatus(),
                                   0,                     // 消費データなし
                                   bytesProduced);        // close_notifyの長さ
      }
#endif
      // close_notify を生成する
      wrapCloseNotify();

      // もうこれ以上出力するデータはない
      setOutboundDone(true);

      // データを出力する
      // この関数の中で wrapResult が設定される
      wrapSecurityContext(destbuff, destlen);
    }
    else
    {
      assert(lht == HELLO_REQUEST || lht == OUTBOUND_CLOSED);

      // InitializeSecurityContext() / AcceptSecurityContext() の結果を出力する
      wrapSecurityContext(destbuff, destlen);
    }
    // ハンドシェーク情報を表示する
    DUMP(_T("NEED_WRAP:output"), destbuff, this->wrapResult.bytesProduced());
  }
  else if (NEED_UNWRAP == hs)
  {
    // 出力データなし
    this->wrapResult.setValues(OK, hs, 0, 0);
  }
  else
  {
    // ハンドシェーク中ではないので、アプリケーションデータを暗号化する
    encryptApplicationData(srcbuff, srclen, destbuff, destlen);
  }

END:
  // ロックを開放
  releaseLock();
  return this->wrapResult;
}

void
SSLEngine::decryptPacketData(void* srcbuff, int srclen, void* destbuff, int destlen)
{
  // 入力データサイズをチェックする
#if 0
  // 以下のチェックは誤っていることが判明
  // ヘッダサイズ+トレーラサイズ より小さくても、DecryptMessage()は成功する
  if (srclen < 0 || ((size_t) srclen < this->streamSizes.cbHeader + this->streamSizes.cbTrailer))
  {
    // 入力データが小さすぎる（ヘッダとトレーラ分すら存在しない）
    this->unwrapResult.setValues(BUFFER_UNDERFLOW,
                                 getHandshakeStatus(),
                                 0,
                                 0);
    return;
  }
#endif

  if (srclen <= 0)
  {
    // 復号対象データなし
    this->unwrapResult.setValues(BUFFER_UNDERFLOW,
                                 getHandshakeStatus(),
                                 0,
                                 0);
    goto END;
  }

  // 最大データサイズを決定する
  // http://support.microsoft.com/?scid=kb%3Ben-us%3B300562&x=13&y=13
  // によると、cbMaximumMessageの値は、SSLの仕様である16384よりも5バイト小さな値16379を
  // 返してくるプラットフォームがある。
  // 上記URLにはPocket PCが含まれていないが、Pocket PC 2003 SEでは同様の問題が発生する。
  // そこで、cbMaximuMessageの値が16384よりも小さい場合には16384を最大値とみなすようにする。
  // EncryptMessage()側で問題が発生する可能性があるため、メンバ変数は書き換えない）
  unsigned long cbMaximumMessage = this->streamSizes.cbMaximumMessage;
  if (cbMaximumMessage < 16384)
  {
    cbMaximumMessage = 16384;
  }

  const size_t max_packet_len = this->streamSizes.cbHeader
                               + cbMaximumMessage
                               + this->streamSizes.cbTrailer;
            
  const size_t data_len = min((size_t) srclen, max_packet_len);
  SecBuffer sbuffers[4];
  assert(this->streamSizes.cBuffers >= 4);

  sbuffers[0].BufferType = SECBUFFER_DATA;
  sbuffers[0].cbBuffer = data_len;
  sbuffers[0].pvBuffer = srcbuff;

  sbuffers[1].BufferType = SECBUFFER_EMPTY;
  sbuffers[2].BufferType = SECBUFFER_EMPTY;
  sbuffers[3].BufferType = SECBUFFER_EMPTY;

  SecBufferDesc msg;
  msg.cBuffers = 4;
  msg.pBuffers = sbuffers;
  msg.ulVersion = SECBUFFER_VERSION;

  ULONG qop;
  SECURITY_STATUS ss
    = pFunctions->DecryptMessage(this->phContext,
                                       &msg,
                                       0,
                                       &qop);
  bool closeNotifyReceived = false;
  size_t pushback_len = 0;
  switch (ss)
  {
  case SEC_E_OK:
    {
      int copy_len = 0;
      for (int i = 0; i < 4; ++i)
      {
        const unsigned long type = sbuffers[i].BufferType;
        const size_t len = sbuffers[i].cbBuffer;
        if (SECBUFFER_DATA == type)
        {
          // 結果をコピーする
          memcpy((char*) destbuff + copy_len, sbuffers[i].pvBuffer, len);
          copy_len += len;
        }
        else if (SECBUFFER_EXTRA == type)
        {
          // 未処理のバイト
          pushback_len += len;
        }

      }
      assert(copy_len <= destlen);

      // http://msdn.microsoft.com/en-us/library/aa375348%28VS.85%29.aspx
      // The message sender has finished using the connection and has initiated a shutdown.
      // For information about initiating or recognizing a shutdown, see Shutting Down an Schannel Connection.
      // Used with the Schannel SSP.
      // Windows 2000 Professional: Schannel returns SEC_E_OK instead of SEC_I_CONTEXT_EXPIRED.
      // If the length of the output buffer is zero and the first byte of the encrypted packet is 0x15,
      // the application can safely assume that the message was a close_notify message and change the return value
      // to SEC_I_CONTEXT_EXPIRED.
      if (! copy_len && 0x15 == *(char*) srcbuff)
      {
        // 相手側から close_notify を受け取った
        closeNotifyReceived = true;
      }
      else
      {
        this->unwrapResult.setValues(OK,
                                     getHandshakeStatus(),
                                     data_len - pushback_len,
                                     copy_len);
      }
    }
    break;

  case SEC_E_BUFFER_TOO_SMALL:
  case SEC_E_INCOMPLETE_MESSAGE:
    {
      // 入力バッファが小さすぎる
      this->unwrapResult.setValues(BUFFER_UNDERFLOW,
                                   getHandshakeStatus(),
                                   0,
                                   0);
    }
    break;

  case SEC_I_RENEGOTIATE:
    // ToDo: 再ネゴシエーション処理
    this->unwrapResult.setError(ss);
    break;

  case SEC_I_CONTEXT_EXPIRED:
    // 相手側から close_notify を受信した
    closeNotifyReceived = true;
    break;

  default:
    this->unwrapResult.setError(ss);
    break;
  }

  if (closeNotifyReceived)
  {
    // close_notifyの処理
    setLastHandshakeTrigger(CLOSE_NOTIFY_RECEIVED);
    this->inboundDone = true;
    // ハンドシェイクステータスを更新
    updateHandshakeStatus();
    this->unwrapResult.setValues(CLOSED,
                                 getHandshakeStatus(),
                                 data_len - pushback_len,
                                 0);
  }

END:
  return;
}


/**
 * 暗号化されたデータを復号する。
 */
const SSLEngineResult&
SSLEngine::unwrap(void* srcbuff, int srclen, void* destbuff, int destlen)
{
  // ロック
  acquireLock();

  if (this->inboundDone)
  {
    // 入力がクローズされている
    this->unwrapResult.setValues(CLOSED, getHandshakeStatus(), 0, 0);
    goto END;
  }

  SSLEngineHandshakeStatus hs = getHandshakeStatus();
  if (! this->phCredential)
  {
    // ハンドシェーク開始
    HRESULT result = beginHandshake();
    if (FAILED(result))
    {
      // ハンドシェーク開始失敗
      this->unwrapResult.setError(result);
      goto END;
    }
  }

  hs = getHandshakeStatus();
  if (NEED_WRAP == hs)
  {
    // ステータスが違う
    this->unwrapResult.setValues(OK, hs, 0, 0);
    goto END;
  }
  else if (NEED_UNWRAP == hs)
  {
    // NEED_UNWRAP
    if (! this->phContext)
    {
      /// コンテキストの初期化を行う
      assert(! getUseClientMode());

      // AcceptSecurityContext()の初回呼び出し
      this->phContext = new CtxtHandle();
      if (! this->phContext)
      {
        this->wrapResult.setError(E_OUTOFMEMORY);
        return this->wrapResult;
      }
      firstAcceptSecurityContext(srcbuff, srclen);
    }
    else if (getLastHandshakeTrigger() == OUTBOUND_CLOSED)
    {
      // close_notifyを送信済みなので、相手側の close_notify を読む
      decryptPacketData(srcbuff, srclen, destbuff, destlen);
      if (srclen > 0)
      {
        assert(isInboundDone());
        assert(NOT_HANDSHAKING == this->handshakeStatus);
      }
    }
    else
    {
      // InitializeSecurityContext()/AcceptSecurityContext()にデータを渡す
      unwrapSecurityContext(srcbuff, srclen);
    }

    DUMP(_T("NEED_UNRAP:input"), srcbuff, srclen);
  }
  else
  {
    // ハンドシェイクは完了しているので、DecryptMessage() でメッセージを復号化する
    decryptPacketData(srcbuff, srclen, destbuff, destlen);
  }

END:
  releaseLock();
  return this->unwrapResult;
}

/**
 * 1回目のInitializeSecurityContext()呼び出しを行う
 */
void
SSLEngine::firstInitializeSecurityContext(void* destbuff, int destlen)
{
  assert(! this->securityContextOutput[0].cbBuffer);

  SecBufferDesc sbdOut;
  
  this->securityContextOutput[0].cbBuffer   = 0;
  this->securityContextOutput[0].BufferType = SECBUFFER_TOKEN;
  this->securityContextOutput[0].pvBuffer   = NULL;

  sbdOut.ulVersion = SECBUFFER_VERSION;
  sbdOut.cBuffers  = 1;
  sbdOut.pBuffers  = this->securityContextOutput;

  unsigned long uAttrOut;
  TimeStamp tsExpiry;

  this->securityStatus
    = pFunctions->InitializeSecurityContext(this->phCredential,
                                NULL,
                                const_cast<SEC_WCHAR*>(this->peerHost),
                                ISC_ATTRIBUTES,
                                0,
                                SECURITY_NETWORK_DREP,
                                NULL,
                                0,
                                this->phContext, 
                                &sbdOut,
                                &uAttrOut,
                                &tsExpiry);

  if (SEC_I_CONTINUE_NEEDED != this->securityStatus)
  {
    this->wrapResult.setError(this->securityStatus);
    return;
  }

  if (this->securityContextOutput[0].pvBuffer && this->securityContextOutput[0].cbBuffer)
  {
    // 値を出力する
    wrapSecurityContext(destbuff, destlen);
  }
}

/**
 * 1回目のAcceptSecurityContext()呼び出しを行う
 */
void
SSLEngine::firstAcceptSecurityContext(void* srcbuff, int srclen)
{
  assert(! this->securityContextOutput[0].cbBuffer);
  assert(this->handshakeStatus == NEED_UNWRAP);

  // 出力バッファを初期化
  SecBufferDesc sbdOut;
  this->securityContextOutput[0].cbBuffer   = 0;
  this->securityContextOutput[0].BufferType = SECBUFFER_TOKEN;
  this->securityContextOutput[0].pvBuffer   = NULL;

  sbdOut.ulVersion = SECBUFFER_VERSION;
  sbdOut.cBuffers  = 1;
  sbdOut.pBuffers  = this->securityContextOutput;

  // 入力バッファを初期化
  SecBufferDesc   sbdIn;
  SecBuffer       sbIn[2];
  sbIn[0].pvBuffer   = srcbuff;
  sbIn[0].cbBuffer   = srclen;
  sbIn[0].BufferType = SECBUFFER_TOKEN;
  sbIn[1].pvBuffer   = NULL;
  sbIn[1].cbBuffer   = 0;
  sbIn[1].BufferType = SECBUFFER_EMPTY;

  sbdIn.ulVersion = SECBUFFER_VERSION;
  sbdIn.cBuffers  = 2;
  sbdIn.pBuffers  = sbIn;
  
  TimeStamp tsExpiry;
  unsigned long uAttrIn = ASC_ATTRIBUTES;
  if (getWantClientAuth() || getNeedClientAuth())
  {
    // クライアント認証
    uAttrIn |= ASC_REQ_MUTUAL_AUTH;
  }
  unsigned long uAttrOut;
  this->securityStatus
    = pFunctions->AcceptSecurityContext(this->phCredential,
                                              NULL,
                                              &sbdIn,
                                              uAttrIn,
                                              SECURITY_NETWORK_DREP,
                                              this->phContext, 
                                              &sbdOut,
                                              &uAttrOut,
                                              &tsExpiry);
#ifdef DEBUG
  {
    LONG flag = uAttrOut & ASC_RET_MUTUAL_AUTH;
    flag = uAttrOut & ASC_RET_EXTENDED_ERROR;
    flag = flag;
  }
#endif

  if (SEC_I_CONTINUE_NEEDED != this->securityStatus)
  {
    this->unwrapResult.setError(this->securityStatus);
    return;
  }
  
  int bytesConsumed = srclen;
  int extra_len = 0;
  if (SECBUFFER_EXTRA == sbIn[1].BufferType)
  {
    // バッファ内にまだ未処理のデータがある
    extra_len = sbIn[1].cbBuffer;
    bytesConsumed -= extra_len;
  }

  if (this->securityContextOutput[0].pvBuffer && this->securityContextOutput[0].cbBuffer)
  {
    assert(! extra_len);
    if (extra_len)
    {
      // ToDo:
      // 現在の実装では、入力バッファに未処理のデータがある状態で
      // 出力データを処理することができない
      this->unwrapResult.setError(HRESULT_FROM_WIN32(ERROR_INTERNAL_ERROR));
      return;
    }

  }

  // Cipher suiteコードを取得する
  if (! getUseClientMode())
  {
    // 生成したデータがServer helloかどうかを調べる
    parseServerHello(this->securityContextOutput[0].pvBuffer, this->securityContextOutput[0].cbBuffer);
  }
  
  updateHandshakeStatus();
  this->unwrapResult.setValues((SEC_E_INCOMPLETE_MESSAGE == this->securityStatus)
                               ? BUFFER_UNDERFLOW : OK,
                               getHandshakeStatus(),
                               bytesConsumed,
                               0
                               );
                              
}

/**
 * InitializeSecurityContext()の出力データをバッファにコピーする
 */
void
SSLEngine::wrapSecurityContext(void* destbuff, int destlen)
{
  assert(this->securityContextOutput[0].pvBuffer && this->securityContextOutput[0].cbBuffer);

  if (destlen < (int) this->securityContextOutput[0].cbBuffer)
  {
    // 出力バッファが足りない
    // NEED_WRAP
    setHandshakeStatus(NEED_WRAP);
    this->wrapResult.setValues(BUFFER_OVERFLOW, // status
                               NEED_WRAP,       // handshakeStatus
                               0,               // bytesConsumed
                               0                // bytesProduced
                               );
  }
  else
  {
    // 出力バッファにコピー
    memcpy(destbuff, this->securityContextOutput[0].pvBuffer, this->securityContextOutput[0].cbBuffer);

    const int bytesProduced = this->securityContextOutput[0].cbBuffer;

    // 出力バッファをクリア
    FreeContextBuffer(this->securityContextOutput[0].pvBuffer);
    memset((void*) this->securityContextOutput, 0, sizeof(this->securityContextOutput));

    // ハンドシェーク状態を更新
    if (! updateHandshakeStatus())
    {
      this->wrapResult.setError(this->securityStatus);
      return;
    }
    
    // 結果を設定
    SSLEngineStatus stat = getLastHandshakeTrigger() == OUTBOUND_CLOSED ? CLOSED : OK;
    this->wrapResult.setValues(stat,
                               getHandshakeStatus(),      // ハンドシェークステータス
                               0,                         // 入力バッファの内容は読んでいない
                               bytesProduced              // 生成バイト数
                               );

  }
}

/**
 * 2回目以降のInitializeSecurityContext()/AcceptSecurityContext()呼び出しを行う
 */
void
SSLEngine::unwrapSecurityContext(void* srcbuff, int srclen)
{
  // 出力バッファには何も残っていないはず
  assert(!this->securityContextOutput[0].cbBuffer);

  char* buff = (char*) srcbuff;
  int len = srclen;

  unsigned long iscAttributes = ISC_ATTRIBUTES;
  unsigned long uAttrOut;
  TimeStamp tsExpiry;

  int bytesConsumed = 0;
  for (;;)
  {
    SecBuffer inBuffer[2];
    inBuffer[0].pvBuffer   = buff;
    inBuffer[0].cbBuffer   = len;
    inBuffer[0].BufferType = SECBUFFER_TOKEN;
    inBuffer[1].pvBuffer   = NULL;
    inBuffer[1].cbBuffer   = 0;
    inBuffer[1].BufferType = SECBUFFER_EMPTY;

    SecBufferDesc sbdIn;
    sbdIn.ulVersion = SECBUFFER_VERSION;
    sbdIn.cBuffers  = 2;
    sbdIn.pBuffers  = inBuffer;

    this->securityContextOutput[0].cbBuffer   = 0;
    this->securityContextOutput[0].BufferType = SECBUFFER_TOKEN;
    this->securityContextOutput[0].pvBuffer   = NULL;

    SecBufferDesc sbdOut;
    sbdOut.ulVersion = SECBUFFER_VERSION;
    sbdOut.cBuffers  = 1;
    sbdOut.pBuffers  = this->securityContextOutput;

    if (getUseClientMode())
    {
      this->securityStatus
        = pFunctions->InitializeSecurityContext(this->phCredential,
                                    this->phContext,
                                    const_cast<SEC_WCHAR*>(getPeerHost()),
                                    iscAttributes,
                                    0,
                                    SECURITY_NETWORK_DREP,
                                    &sbdIn,
                                    0,
                                    this->phContext,
                                    &sbdOut,
                                    &uAttrOut,
                                    &tsExpiry);
      if (! (ISC_RET_CONFIDENTIALITY & uAttrOut))
      {
        // 通信内容が暗号化できていない
        this->securityStatus = SEC_E_SECURITY_QOS_FAILED;
        break;
      }
    }
    else
    {
      unsigned long uAttrIn = ASC_ATTRIBUTES;
      if (getWantClientAuth() || getNeedClientAuth())
      {
        uAttrIn |= ASC_REQ_MUTUAL_AUTH;
      }
      this->securityStatus
        = pFunctions->AcceptSecurityContext(this->phCredential,
                                  this->phContext,
                                  &sbdIn,
                                  uAttrIn,
                                  SECURITY_NETWORK_DREP,
                                  this->phContext,
                                  &sbdOut,
                                  &uAttrOut,
                                  &tsExpiry);
      if (! (ASC_RET_CONFIDENTIALITY & uAttrOut))
      {
        // 通信内容が暗号化できていない
        this->securityStatus = SEC_E_SECURITY_QOS_FAILED;
        break;
      }
    }
    if (FAILED(this->securityStatus))
    {
      break;
    }

    // Cipher suiteコードを取得する
    if (getUseClientMode())
    {
      // 受信したデータがServer helloかどうかを調べる
      parseServerHello(buff, len);
    }

    int extra_len = 0;
    if (SECBUFFER_EXTRA == inBuffer[1].BufferType)
    {
      // バッファ内にまだ未処理のデータがある
      extra_len = inBuffer[1].cbBuffer;
    }
    // バッファ内のデータ消費量
    bytesConsumed += (len - extra_len);
    
    if (this->securityContextOutput[0].pvBuffer && this->securityContextOutput[0].cbBuffer)
    {
      assert(! extra_len);
      if (extra_len)
      {
        // ToDo:
        // 現在の実装では、入力バッファに未処理のデータがある状態で
        // 出力データを処理することができない
        this->unwrapResult.setError(HRESULT_FROM_WIN32(ERROR_INTERNAL_ERROR));
        return;
      }
      updateHandshakeStatus();
      this->unwrapResult.setValues(OK,
                                   getHandshakeStatus(),
                                   bytesConsumed,
                                   0
                                   );
      return;
    }

    // 出力データなし
    if (SEC_I_CONTINUE_NEEDED == this->securityStatus)
    {
      // ハンドシェイク継続
      if (extra_len)
      {
        // バッファ内の未処理データを再度 InitializeSecurityContext()/AcceptSecurityContext() に渡す
        buff += (len - extra_len);
        len = extra_len;
      }
      else
      {
        break;
      }
    }
    else if (SEC_I_INCOMPLETE_CREDENTIALS == this->securityStatus && getUseClientMode())
    {
      // クライアント認証を要求されている
      // もう一度 InitializeSecurityContext() を呼び出すので、バッファ内のデータ消費量を
      // 元に戻す
      bytesConsumed -= (len - extra_len);

      // ローカル証明書がある場合、Credentials Handleを再作成する
      if (this->pLocalCertificate)
      {
        // 現在のCredentialsHandleを開放
        assert(this->phCredential);
        FreeCredentialsHandle(this->phCredential);

        SCHANNEL_CRED sslCred = {0};
        sslCred.dwVersion = SCHANNEL_CRED_VERSION;

        // ローカル証明書を設定する
        sslCred.cCreds = 1;
        sslCred.paCred = &this->pLocalCertificate;

        // クライアント認証あり、サーバ証明書の自動検証なし
        sslCred.dwFlags = SCH_CRED_USE_DEFAULT_CREDS | 
                            SCH_CRED_MANUAL_CRED_VALIDATION;

        // サポートするプロトコル
        sslCred.grbitEnabledProtocols = this->enabledProtocols;

        // SSPI クレデンシャルの取得
        TimeStamp tsExpiry;
        this->securityStatus
          = pFunctions->AcquireCredentialsHandle(
                                    NULL, 
                                    UNISP_NAME, 
                                    SECPKG_CRED_OUTBOUND,
                                    NULL, 
                                    &sslCred, 
                                    NULL,
                                    NULL,
                                    this->phCredential,
                                    &tsExpiry);
        if (FAILED(this->securityStatus))
        {
          break;
        }
      }
      else
      {
        // InitializeSecurityContext()に渡すフラグに ISC_REQ_USE_SUPPLIED_CREDSを追加する
        // ------------------------------------------------------------------------------
        // http://www.derkeiler.com/Newsgroups/microsoft.public.platformsdk.security/2004-08/0187.html
        // #2
        // It can skip finding a client certificate, and just call
        // InitializeSecurityContext back, passing in the ISC_REQ_USE_SUPPLIED_CRED
        // flag. This will tell schannel to send the client certificate associated with
        // the passed in cred handle, regardless of whether the cred handle actually
        // contains a client certificate or not. If the credential doesn't contain a
        // client certificate, then the handshake is completed without sending it. 
        // ------------------------------------------------------------------------------
        // ただし、このフラグを追加しなくても Windows Mobile 5.0は正常に動作する
        iscAttributes |= ISC_REQ_USE_SUPPLIED_CREDS;

      }
    }
    else
    {
      break;
    }
  }

  // ハンドシェークステータスを更新する
  if (! updateHandshakeStatus())
  {
    this->unwrapResult.setError(this->securityStatus);
  }
  else
  {
    this->unwrapResult.setValues((SEC_E_INCOMPLETE_MESSAGE == this->securityStatus) ? BUFFER_UNDERFLOW : OK,
                                 getHandshakeStatus(),
                                 bytesConsumed,
                                 0);
  }
}

/**
 * ハンドシェイク状態を更新する
 */
bool
SSLEngine::updateHandshakeStatus()
{
  // 入出力ともに閉じてしまった
  if (isOutboundDone() && isInboundDone())
  {
    setHandshakeStatus(NOT_HANDSHAKING);
    return true;
  }

  if (! this->pPeerCertificate)
  {
    // 証明書情報を得る
    SECURITY_STATUS ss
      = pFunctions->QueryContextAttributes(this->phContext,
                                SECPKG_ATTR_REMOTE_CERT_CONTEXT,
                                &this->pPeerCertificate);
    if (this->pPeerCertificate)
    {
      // 証明書の検証が必要なので、NEED_TASKを設定する
      this->delegatedTaskCount = 1;
      setHandshakeStatus(NEED_TASK);
      return true;
    }
  }

  // 書き込むべきデータが保留状態になっている
  if (this->securityContextOutput[0].pvBuffer && this->securityContextOutput[0].cbBuffer)
  {
    setHandshakeStatus(NEED_WRAP);
    return true;
  }

  SSLEngineHandshakeTrigger lht = getLastHandshakeTrigger();
  switch (lht)
  {
  case OUTBOUND_CLOSED:
    // 相手側に close_notify を送信
    {
      // この時点で出力データはもう存在しなくなっている
      // (close_notifyを送信した）
      setOutboundDone(true);

      if (isInboundDone())
      {
        // 入力データも存在しないので、ハンドシェーク終了
        setHandshakeStatus(NOT_HANDSHAKING);
      }
      else
      {
        // 入力は生きているので、close_notify を受信する
        setHandshakeStatus(NEED_UNWRAP);
      }
    }
    return true;

  case CLOSE_NOTIFY_RECEIVED:
    // close_notifyを受信した状態
    {
      if (isOutboundDone())
      {
        // すでに出力が閉じているため、終了する
        // (close_notify送信済み）
        setHandshakeStatus(NOT_HANDSHAKING);
      }
      else
      {
        // close_notifyを送る
        setHandshakeStatus(NEED_WRAP);
      }
    }
    return true;

  case HELLO_REQUEST:
    {
      // 直前のInitializeSecurityContext() / AcceptSecurityContext() 呼び出し結果から判断する
      const SECURITY_STATUS ss = this->securityStatus;

      // 正常終了
      if (SEC_E_OK == ss)
      {
        // ハンドシェイク完了
        setHandshakeStatus(FINISHED);

        // ストリームサイズを取得する
        pFunctions->QueryContextAttributes(this->phContext, 
                                                     SECPKG_ATTR_STREAM_SIZES, 
                                                     &this->streamSizes);

        if (! this->peerCertificateChainLoaded)
        {
          // 相手側の証明書が存在しない
          if (! getUseClientMode() && ! getNeedClientAuth())
          {
            // 証明書は不要
            return true;
          }
          else
          {
            // 証明書が必須なのに存在しない
            this->securityStatus = SEC_E_INCOMPLETE_CREDENTIALS;
            return false;
          }
        }
        else
        {
          return true;
        }
      }

      if (SEC_I_INCOMPLETE_CREDENTIALS == ss)
      {
        // この状態は想定外(unwrap()内で処理できているはず）
        return false;
      }

      if (SEC_E_INCOMPLETE_MESSAGE == ss)
      {
        // ネットワークからデータを読む必要がある
        setHandshakeStatus(NEED_UNWRAP);
        return true;
      }

      if (FAILED(ss))
      {
        // 何かエラーが起きた
        return false;
      }

      // 上記以外の場合は、ネットワークからデータを読み込む必要があるので
      // NEED_UNWRAPを設定する
      setHandshakeStatus(NEED_UNWRAP);
    }
    return true;
  
  default:
    assert(false);
    return false;
  }
}

/**
 * ハンドシェークを開始する
 */
HRESULT
SSLEngine::beginHandshake()
{
  if (getHandshakeStatus() != NOT_HANDSHAKING || this->phCredential)
  {
    // ハンドシェークはすでに開始している（または完了している）
    return S_OK;
  }

  // CredHandleを初期化していく
  this->phCredential = new CredHandle();
  SCHANNEL_CRED sslCred = {0};
  sslCred.dwVersion = SCHANNEL_CRED_VERSION;

  // ローカル証明書を設定する
  if (! getUseClientMode() && this->pLocalCertificate)
  {
    sslCred.cCreds = 1;
    sslCred.paCred = &this->pLocalCertificate;
  }

  if (getUseClientMode())
  {
    // クライアント認証なし、サーバ証明書の自動検証なし
    sslCred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS | 
                      SCH_CRED_MANUAL_CRED_VALIDATION;
  }

  // サポートするプロトコル
  sslCred.grbitEnabledProtocols = this->enabledProtocols;

  // SSPI クレデンシャルの取得
  TimeStamp tsExpiry;
  SECURITY_STATUS ss
    = pFunctions->AcquireCredentialsHandle(
                              NULL, 
                              UNISP_NAME, 
                              getUseClientMode() ? SECPKG_CRED_OUTBOUND : SECPKG_CRED_INBOUND,
                              NULL, 
                              &sslCred, 
                              NULL,
                              NULL,
                              this->phCredential,
                              &tsExpiry);

  if (SUCCEEDED(ss))
  {
    if (getUseClientMode())
    {
      // サーバへClient helloを送信する必要がある
      setHandshakeStatus(NEED_WRAP);
    }
    else
    {
      // クライアントからのClient helloを受信する必要がある
      setHandshakeStatus(NEED_UNWRAP);
    }
  }
  else
  {
    // CredHandleを削除する
    ::FreeCredentialsHandle(this->phCredential);
    delete this->phCredential;
    this->phCredential = NULL;
  }

  return ss;
}

/**
 * 委譲タスクが存在するかを返す
 */
bool
SSLEngine::hasDelegatedTask() 
{
  return this->delegatedTaskCount > 0;
}

/**
 * 委譲タスクがある場合、そのタスクを実行する
 */
SSLEngineTaskStatus
SSLEngine::executeDelegatedTask()
{
  if (! hasDelegatedTask())
  {
    // 委譲タスクが存在しない
    assert(false);
    return TASK_ILLEGAL_STATE_ERROR;
  }

  acquireLock();

  // 委譲タスクを実行する
  // 今のところ1種類しかタスクがないので、ここで直接実行してしまう
  this->delegatedTaskCount = 0;

  // 証明書を検証する
  bool result = loadPeerCertificateChain();
  // ハンドシェイクステータスを更新
  updateHandshakeStatus();


  releaseLock();

  if (! result)
  {
    return TASK_CERTIFICATE_VERIFY_ERROR;
  }

  return TASK_OK;
}

/**
 * 指定されたデータがServer helloである場合、
 * Cipher suiteをあらわすコードを抽出する
 */
bool
SSLEngine::parseServerHello(void* buff, int len)
{
  bool result = false;
  char* work = (char*) buff;
  if (len > 5)
  {
    if ((char) 0x16 == *work)
    {
      // handshake
      work += 3;
      unsigned short fragment_length = ((unsigned short) *work << 8) | (*(work+1) & 0xff);
      if (len >= 5 + fragment_length)
      {
        work += 2;
        if (0x02 == *work)
        {
          // server hello

          work += 4;  // Skip length (high, mid, low)
          this->protocolVersion = ((unsigned short) *work << 8) | (*(work + 1) & 0xff);

          work += 2 + 4 + 28;  // Skip gmt_unix_time and server_random.

          // Get session ID.
          this->sessionIDLength = (unsigned char) *work++;
          this->sessionID = new unsigned char[this->sessionIDLength];
          if (this->sessionID)
          {
            memcpy(this->sessionID, work, this->sessionIDLength);
          }

          work += this->sessionIDLength;

          // cipherSuite code.
          this->cipherSuiteCode = ((unsigned short) *work << 8) | (*(work + 1) & 0xff);

          result = true;
        }
      }
    }
  }
  return result;
}

/**
 * 自局側の証明書を設定する
 */
void
SSLEngine::setLocalCertificate(PCCERT_CONTEXT localCert)
{
  // 複製は作成しない
  this->pLocalCertificate = localCert;
}