// BcasCard.cpp: CBcasCard クラスのインプリメンテーション // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include #include "memory.h" #include "BcasCard.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif //#pragma comment(lib, "WinScard.lib") using std::auto_ptr; CBcasCard::CBcasCard() : m_hBcasCard(NULL) , m_bIsEstablish(false) , m_dwLastError(BCEC_NOERROR) { // 内部状態初期化 ::ZeroMemory(&m_BcasCardInfo, sizeof(m_BcasCardInfo)); ::ZeroMemory(&m_EcmStatus, sizeof(m_EcmStatus)); // リソースマネージャコンテキスト確立 if(::SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &m_ScardContext) != SCARD_S_SUCCESS){ m_dwLastError = BCEC_NOTESTABLISHED; } else{ m_bIsEstablish = true; // カードリーダ列挙 EnumCardReader(); } } CBcasCard::~CBcasCard() { CloseCard(); // リソースマネージャコンテキストの開放 if(m_bIsEstablish)::SCardReleaseContext(m_ScardContext); } const DWORD CBcasCard::GetCardReaderNum(void) const { // カードリーダー数を返す return m_CardReaderArray.size(); } LPCTSTR CBcasCard::GetCardReaderName(const DWORD dwIndex) const { // カードリーダー名を返す return (dwIndex < GetCardReaderNum())? m_CardReaderArray[dwIndex].c_str() : NULL; } const bool CBcasCard::OpenCard(LPCTSTR lpszReader) { // リソースマネージャコンテキストの確立 if(!m_bIsEstablish){ m_dwLastError = BCEC_NOTESTABLISHED; return false; } // 一旦クローズする CloseCard(); if(lpszReader){ // 指定されたカードリーダに対してオープンを試みる DWORD dwActiveProtocol = SCARD_PROTOCOL_UNDEFINED; if(::SCardConnect(m_ScardContext, lpszReader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T1, &m_hBcasCard, &dwActiveProtocol) != SCARD_S_SUCCESS){ m_dwLastError = BCEC_CARDOPENERROR; return false; } if(dwActiveProtocol != SCARD_PROTOCOL_T1){ CloseCard(); m_dwLastError = BCEC_CARDOPENERROR; return false; } } else{ // 全てのカードリーダに対してオープンを試みる DWORD dwIndex = 0UL; while(GetCardReaderName(dwIndex)){ if(OpenCard(GetCardReaderName(dwIndex++)))return true; } return false; } // カード初期化 if(!InitialSetting())return false; m_dwLastError = BCEC_NOERROR; return true; } void CBcasCard::CloseCard(void) { // カードをクローズする if(m_hBcasCard){ ::SCardDisconnect(m_hBcasCard, SCARD_LEAVE_CARD); m_hBcasCard = NULL; } } const DWORD CBcasCard::GetLastError(void) const { // 最後に発生したエラーを返す return m_dwLastError; } const bool CBcasCard::EnumCardReader(void) { // カードリーダを列挙する DWORD dwBuffSize = 0UL; switch(::SCardListReaders(m_ScardContext, NULL, NULL, &dwBuffSize)){ case SCARD_E_NO_READERS_AVAILABLE : // カードリーダが見つからない m_dwLastError = BCEC_NOCARDREADERS; return false; case SCARD_S_SUCCESS : // バッファサイズ取得成功 break; default: // エラー m_dwLastError = BCEC_INTERNALERROR; return false; } // バッファ確保 auto_ptr szReaders(new TCHAR[dwBuffSize]); switch(::SCardListReaders(m_ScardContext, NULL, szReaders.get(), &dwBuffSize)){ case SCARD_E_NO_READERS_AVAILABLE : // カードリーダが見つからない m_dwLastError = BCEC_NOCARDREADERS; return false; case SCARD_S_SUCCESS : { // カードリーダ名保存 LPTSTR lpszCurReader = szReaders.get(); m_CardReaderArray.clear(); while(*lpszCurReader){ m_CardReaderArray.push_back(lpszCurReader); lpszCurReader += m_CardReaderArray.back().length() + 1UL; } break; } default: // エラー m_dwLastError = BCEC_INTERNALERROR; return false; } m_dwLastError = BCEC_NOERROR; return true; } const bool CBcasCard::TransmitCommand(const BYTE *pSendData, const DWORD dwSendSize, BYTE *pRecvData, const DWORD dwMaxRecv, DWORD *pdwRecvSize) { DWORD dwRecvSize = dwMaxRecv; // データ送受信 DWORD dwReturn = ::SCardTransmit(m_hBcasCard, SCARD_PCI_T1, pSendData, dwSendSize, NULL, pRecvData, &dwRecvSize); // 受信サイズ格納 if(pdwRecvSize)*pdwRecvSize = dwRecvSize; return (dwReturn == SCARD_S_SUCCESS)? true : false; } const bool CBcasCard::InitialSetting(void) { static const BYTE InitSettingCmd[] = {0x90U, 0x30U, 0x00U, 0x00U, 0x00U}; // 「Initial Setting Conditions Command」を処理する if(!m_hBcasCard){ m_dwLastError = BCEC_CARDNOTOPEN; return false; } // バッファ準備 DWORD dwRecvSize = 0UL; BYTE RecvData[1024]; ::ZeroMemory(RecvData, sizeof(RecvData)); // コマンド送信 if(!TransmitCommand(InitSettingCmd, sizeof(InitSettingCmd), RecvData, sizeof(RecvData), &dwRecvSize)){ m_dwLastError = BCEC_TRANSMITERROR; return false; } if(dwRecvSize < 57UL){ m_dwLastError = BCEC_TRANSMITERROR; return false; } // レスポンス解析 ::CopyMemory(m_BcasCardInfo.BcasCardID, &RecvData[8], 6UL); // +8 Card ID ::CopyMemory(m_BcasCardInfo.SystemKey, &RecvData[16], 32UL); // +16 Descrambling system key ::CopyMemory(m_BcasCardInfo.InitialCbc, &RecvData[48], 8UL); // +48 Descrambler CBC initial value // ECMステータス初期化 ::ZeroMemory(&m_EcmStatus, sizeof(m_EcmStatus)); return true; } const BYTE * CBcasCard::GetBcasCardID(void) { // Card ID を返す if(!m_hBcasCard){ m_dwLastError = BCEC_CARDNOTOPEN; return NULL; } m_dwLastError = BCEC_NOERROR; return m_BcasCardInfo.BcasCardID; } const BYTE * CBcasCard::GetInitialCbc(void) { // Descrambler CBC Initial Value を返す if(!m_hBcasCard){ m_dwLastError = BCEC_CARDNOTOPEN; return NULL; } m_dwLastError = BCEC_NOERROR; return m_BcasCardInfo.InitialCbc; } const BYTE * CBcasCard::GetSystemKey(void) { // Descrambling System Key を返す if(!m_hBcasCard){ m_dwLastError = BCEC_CARDNOTOPEN; return NULL; } m_dwLastError = BCEC_NOERROR; return m_BcasCardInfo.SystemKey; } const BYTE * CBcasCard::GetKsFromEcm(const BYTE *pEcmData, const DWORD dwEcmSize) { static const BYTE EcmReceiveCmd[] = {0x90U, 0x34U, 0x00U, 0x00U}; // 「ECM Receive Command」を処理する if(!m_hBcasCard){ m_dwLastError = BCEC_CARDNOTOPEN; return NULL; } // ECMサイズをチェック if(!pEcmData || (dwEcmSize < 30UL) || (dwEcmSize > 256UL)){ m_dwLastError = BCEC_BADARGUMENT; return NULL; } // キャッシュをチェックする if(!StoreEcmData(pEcmData, dwEcmSize)){ // ECMが同一の場合はキャッシュ済みKsを返す m_dwLastError = BCEC_NOERROR; return m_EcmStatus.KsData; } // バッファ準備 DWORD dwRecvSize = 0UL; BYTE SendData[1024]; BYTE RecvData[1024]; ::ZeroMemory(RecvData, sizeof(RecvData)); // コマンド構築 ::CopyMemory(SendData, EcmReceiveCmd, sizeof(EcmReceiveCmd)); // CLA, INS, P1, P2 SendData[sizeof(EcmReceiveCmd)] = (BYTE)dwEcmSize; // COMMAND DATA LENGTH ::CopyMemory(&SendData[sizeof(EcmReceiveCmd) + 1], pEcmData, dwEcmSize); // ECM SendData[sizeof(EcmReceiveCmd) + dwEcmSize + 1] = 0x00U; // RESPONSE DATA LENGTH // コマンド送信 if(!TransmitCommand(SendData, sizeof(EcmReceiveCmd) + dwEcmSize + 2UL, RecvData, sizeof(RecvData), &dwRecvSize)){ ::ZeroMemory(&m_EcmStatus, sizeof(m_EcmStatus)); m_dwLastError = BCEC_TRANSMITERROR; return NULL; } // サイズチェック if(dwRecvSize != 25UL){ ::ZeroMemory(&m_EcmStatus, sizeof(m_EcmStatus)); m_dwLastError = BCEC_TRANSMITERROR; return NULL; } // レスポンス解析 ::CopyMemory(m_EcmStatus.KsData, &RecvData[6], sizeof(m_EcmStatus.KsData)); // リターンコード解析 switch(((WORD)RecvData[4] << 8) | (WORD)RecvData[5]){ // Purchased: Viewing case 0x0200U : // Payment-deferred PPV case 0x0400U : // Prepaid PPV case 0x0800U : // Tier m_dwLastError = BCEC_NOERROR; return m_EcmStatus.KsData; // 上記以外(視聴不可) default : m_dwLastError = BCEC_ECMREFUSED; return NULL; } } const bool CBcasCard::StoreEcmData(const BYTE *pEcmData, const DWORD dwEcmSize) { bool bUpdate = false; // ECMデータ比較 if(m_EcmStatus.dwLastEcmSize != dwEcmSize){ // サイズが変化した bUpdate = true; } else{ // サイズが同じ場合はデータをチェックする for(DWORD dwPos = 0UL ; dwPos < dwEcmSize ; dwPos++){ if(pEcmData[dwPos] != m_EcmStatus.LastEcmData[dwPos]){ // データが不一致 bUpdate = true; break; } } } // ECMデータを保存する if(bUpdate){ m_EcmStatus.dwLastEcmSize = dwEcmSize; ::CopyMemory(m_EcmStatus.LastEcmData, pEcmData, dwEcmSize); } return bUpdate; }