libaribb25/aribb25/b_cas_card.c

730 lines
16 KiB
C

#include "b_cas_card.h"
#include "b_cas_card_error_code.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <winscard.h>
#if defined(_WIN32)
# include <windows.h>
# include <tchar.h>
#else
# define TCHAR char
# define _tcslen strlen
# if !defined(__CYGWIN__)
# include <wintypes.h>
# endif
# define _tcslen strlen
#endif
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
inner structures
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
typedef struct {
SCARDCONTEXT mng;
SCARDHANDLE card;
uint8_t *pool;
LPTSTR reader;
uint8_t *sbuf;
uint8_t *rbuf;
B_CAS_INIT_STATUS stat;
B_CAS_ID id;
int32_t id_max;
B_CAS_PWR_ON_CTRL_INFO pwc;
int32_t pwc_max;
} B_CAS_CARD_PRIVATE_DATA;
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
constant values
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static const uint8_t INITIAL_SETTING_CONDITIONS_CMD[] = {
0x90, 0x30, 0x00, 0x00, 0x00,
};
static const uint8_t CARD_ID_INFORMATION_ACQUIRE_CMD[] = {
0x90, 0x32, 0x00, 0x00, 0x00,
};
static const uint8_t POWER_ON_CONTROL_INFORMATION_REQUEST_CMD[] = {
0x90, 0x80, 0x00, 0x00, 0x01, 0x00, 0x00,
};
static const uint8_t ECM_RECEIVE_CMD_HEADER[] = {
0x90, 0x34, 0x00, 0x00,
};
static const uint8_t EMM_RECEIVE_CMD_HEADER[] = {
0x90, 0x36, 0x00, 0x00,
};
#define B_CAS_BUFFER_MAX (4*1024)
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
function prottypes (interface method)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void release_b_cas_card(void *bcas);
static int init_b_cas_card(void *bcas);
static int get_init_status_b_cas_card(void *bcas, B_CAS_INIT_STATUS *stat);
static int get_id_b_cas_card(void *bcas, B_CAS_ID *dst);
static int get_pwr_on_ctrl_b_cas_card(void *bcas, B_CAS_PWR_ON_CTRL_INFO *dst);
static int proc_ecm_b_cas_card(void *bcas, B_CAS_ECM_RESULT *dst, uint8_t *src, int len);
static int proc_emm_b_cas_card(void *bcas, uint8_t *src, int len);
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
global function implementation
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
B_CAS_CARD *create_b_cas_card(void)
{
int n;
B_CAS_CARD *r;
B_CAS_CARD_PRIVATE_DATA *prv;
n = sizeof(B_CAS_CARD) + sizeof(B_CAS_CARD_PRIVATE_DATA);
prv = (B_CAS_CARD_PRIVATE_DATA *)calloc(1, n);
if(prv == NULL){
return NULL;
}
r = (B_CAS_CARD *)(prv+1);
r->private_data = prv;
r->release = release_b_cas_card;
r->init = init_b_cas_card;
r->get_init_status = get_init_status_b_cas_card;
r->get_id = get_id_b_cas_card;
r->get_pwr_on_ctrl = get_pwr_on_ctrl_b_cas_card;
r->proc_ecm = proc_ecm_b_cas_card;
r->proc_emm = proc_emm_b_cas_card;
return r;
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
function prottypes (private method)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static B_CAS_CARD_PRIVATE_DATA *private_data(void *bcas);
static void teardown(B_CAS_CARD_PRIVATE_DATA *prv);
static int change_id_max(B_CAS_CARD_PRIVATE_DATA *prv, int max);
static int change_pwc_max(B_CAS_CARD_PRIVATE_DATA *prv, int max);
static int connect_card(B_CAS_CARD_PRIVATE_DATA *prv, LPCTSTR reader_name);
static void extract_power_on_ctrl_response(B_CAS_PWR_ON_CTRL *dst, uint8_t *src);
static void extract_mjd(int *yy, int *mm, int *dd, int mjd);
static int setup_ecm_receive_command(uint8_t *dst, uint8_t *src, int len);
static int setup_emm_receive_command(uint8_t *dst, uint8_t *src, int len);
static int32_t load_be_uint16(uint8_t *p);
static int64_t load_be_uint48(uint8_t *p);
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
interface method implementation
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void release_b_cas_card(void *bcas)
{
B_CAS_CARD_PRIVATE_DATA *prv;
prv = private_data(bcas);
if(prv == NULL){
/* do nothing */
return;
}
teardown(prv);
free(prv);
}
static int init_b_cas_card(void *bcas)
{
int m;
long ret;
unsigned long len;
B_CAS_CARD_PRIVATE_DATA *prv;
prv = private_data(bcas);
if(prv == NULL){
return B_CAS_CARD_ERROR_INVALID_PARAMETER;
}
teardown(prv);
ret = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &(prv->mng));
if(ret != SCARD_S_SUCCESS){
return B_CAS_CARD_ERROR_NO_SMART_CARD_READER;
}
ret = SCardListReaders(prv->mng, NULL, NULL, &len);
if(ret != SCARD_S_SUCCESS){
return B_CAS_CARD_ERROR_NO_SMART_CARD_READER;
}
len += 256;
m = (sizeof(TCHAR)*len) + (2*B_CAS_BUFFER_MAX) + (sizeof(int64_t)*16) + (sizeof(B_CAS_PWR_ON_CTRL)*16);
prv->pool = (uint8_t *)malloc(m);
if(prv->pool == NULL){
return B_CAS_CARD_ERROR_NO_ENOUGH_MEMORY;
}
prv->reader = (LPTSTR)(prv->pool);
prv->sbuf = prv->pool + len;
prv->rbuf = prv->sbuf + B_CAS_BUFFER_MAX;
prv->id.data = (int64_t *)(prv->rbuf + B_CAS_BUFFER_MAX);
prv->id_max = 16;
prv->pwc.data = (B_CAS_PWR_ON_CTRL *)(prv->id.data + prv->id_max);
prv->pwc_max = 16;
ret = SCardListReaders(prv->mng, NULL, prv->reader, &len);
if(ret != SCARD_S_SUCCESS){
return B_CAS_CARD_ERROR_NO_SMART_CARD_READER;
}
while( prv->reader[0] != 0 ){
if(connect_card(prv, prv->reader)){
break;
}
prv->reader += (_tcslen(prv->reader) + 1);
}
if(prv->card == 0){
return B_CAS_CARD_ERROR_ALL_READERS_CONNECTION_FAILED;
}
return 0;
}
static int get_init_status_b_cas_card(void *bcas, B_CAS_INIT_STATUS *stat)
{
B_CAS_CARD_PRIVATE_DATA *prv;
prv = private_data(bcas);
if( (prv == NULL) || (stat == NULL) ){
return B_CAS_CARD_ERROR_INVALID_PARAMETER;
}
if(prv->card == 0){
return B_CAS_CARD_ERROR_NOT_INITIALIZED;
}
memcpy(stat, &(prv->stat), sizeof(B_CAS_INIT_STATUS));
return 0;
}
static int get_id_b_cas_card(void *bcas, B_CAS_ID *dst)
{
long ret;
unsigned long slen;
unsigned long rlen;
int i,num;
uint8_t *p;
uint8_t *tail;
B_CAS_CARD_PRIVATE_DATA *prv;
prv = private_data(bcas);
if( (prv == NULL) || (dst == NULL) ){
return B_CAS_CARD_ERROR_INVALID_PARAMETER;
}
if(prv->card == 0){
return B_CAS_CARD_ERROR_NOT_INITIALIZED;
}
slen = sizeof(CARD_ID_INFORMATION_ACQUIRE_CMD);
memcpy(prv->sbuf, CARD_ID_INFORMATION_ACQUIRE_CMD, slen);
rlen = B_CAS_BUFFER_MAX;
ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, slen, NULL, prv->rbuf, &rlen);
if( (ret != SCARD_S_SUCCESS) || (rlen < 19) ){
return B_CAS_CARD_ERROR_TRANSMIT_FAILED;
}
p = prv->rbuf + 6;
tail = prv->rbuf + rlen;
if( p+1 > tail ){
return B_CAS_CARD_ERROR_TRANSMIT_FAILED;
}
num = p[0];
if(num > prv->id_max){
if(change_id_max(prv, num+4) < 0){
return B_CAS_CARD_ERROR_NO_ENOUGH_MEMORY;
}
}
p += 1;
for(i=0;i<num;i++){
if( p+10 > tail ){
return B_CAS_CARD_ERROR_TRANSMIT_FAILED;
}
prv->id.data[i] = load_be_uint48(p+2);
p += 10;
}
prv->id.count = num;
memcpy(dst, &(prv->id), sizeof(B_CAS_ID));
return 0;
}
static int get_pwr_on_ctrl_b_cas_card(void *bcas, B_CAS_PWR_ON_CTRL_INFO *dst)
{
long ret;
unsigned long slen;
unsigned long rlen;
int i,num,code;
B_CAS_CARD_PRIVATE_DATA *prv;
memset(dst, 0, sizeof(B_CAS_PWR_ON_CTRL_INFO));
prv = private_data(bcas);
if( (prv == NULL) || (dst == NULL) ){
return B_CAS_CARD_ERROR_INVALID_PARAMETER;
}
if(prv->card == 0){
return B_CAS_CARD_ERROR_NOT_INITIALIZED;
}
slen = sizeof(POWER_ON_CONTROL_INFORMATION_REQUEST_CMD);
memcpy(prv->sbuf, POWER_ON_CONTROL_INFORMATION_REQUEST_CMD, slen);
prv->sbuf[5] = 0;
rlen = B_CAS_BUFFER_MAX;
ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, slen, NULL, prv->rbuf, &rlen);
if( (ret != SCARD_S_SUCCESS) || (rlen < 18) || (prv->rbuf[6] != 0) ){
return B_CAS_CARD_ERROR_TRANSMIT_FAILED;
}
code = load_be_uint16(prv->rbuf+4);
if(code == 0xa101){
/* no data */
return 0;
}else if(code != 0x2100){
return B_CAS_CARD_ERROR_TRANSMIT_FAILED;
}
num = (prv->rbuf[7] + 1);
if(prv->pwc_max < num){
if(change_pwc_max(prv, num+4) < 0){
return B_CAS_CARD_ERROR_NO_ENOUGH_MEMORY;
}
}
extract_power_on_ctrl_response(prv->pwc.data+0, prv->rbuf);
for(i=1;i<num;i++){
prv->sbuf[5] = (uint8_t)i;
rlen = B_CAS_BUFFER_MAX;
ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, slen, NULL, prv->rbuf, &rlen);
if( (ret != SCARD_S_SUCCESS) || (rlen < 18) || (prv->rbuf[6] != i) ){
return B_CAS_CARD_ERROR_TRANSMIT_FAILED;
}
extract_power_on_ctrl_response(prv->pwc.data+i, prv->rbuf);
}
prv->pwc.count = num;
memcpy(dst, &(prv->pwc), sizeof(B_CAS_PWR_ON_CTRL_INFO));
return 0;
}
static int proc_ecm_b_cas_card(void *bcas, B_CAS_ECM_RESULT *dst, uint8_t *src, int len)
{
int retry_count;
long ret;
unsigned long slen;
unsigned long rlen;
B_CAS_CARD_PRIVATE_DATA *prv;
prv = private_data(bcas);
if( (prv == NULL) ||
(dst == NULL) ||
(src == NULL) ||
(len < 1) ){
return B_CAS_CARD_ERROR_INVALID_PARAMETER;
}
if(prv->card == 0){
return B_CAS_CARD_ERROR_NOT_INITIALIZED;
}
slen = setup_ecm_receive_command(prv->sbuf, src, len);
rlen = B_CAS_BUFFER_MAX;
retry_count = 0;
ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, slen, NULL, prv->rbuf, &rlen);
while( ((ret != SCARD_S_SUCCESS) || (rlen < 25)) && (retry_count < 2) ){
retry_count += 1;
// if(!connect_card(prv, prv->reader)){
// continue;
// }
// slen = setup_ecm_receive_command(prv->sbuf, src, len);
rlen = B_CAS_BUFFER_MAX;
ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, slen, NULL, prv->rbuf, &rlen);
}
if( (ret != SCARD_S_SUCCESS) || (rlen < 25) ){
return B_CAS_CARD_ERROR_TRANSMIT_FAILED;
}
memcpy(dst->scramble_key, prv->rbuf+6, 16);
dst->return_code = load_be_uint16(prv->rbuf+4);
return 0;
}
static int proc_emm_b_cas_card(void *bcas, uint8_t *src, int len)
{
int retry_count;
long ret;
unsigned long slen;
unsigned long rlen;
B_CAS_CARD_PRIVATE_DATA *prv;
prv = private_data(bcas);
if( (prv == NULL) ||
(src == NULL) ||
(len < 1) ){
return B_CAS_CARD_ERROR_INVALID_PARAMETER;
}
if(prv->card == 0){
return B_CAS_CARD_ERROR_NOT_INITIALIZED;
}
slen = setup_emm_receive_command(prv->sbuf, src, len);
rlen = B_CAS_BUFFER_MAX;
retry_count = 0;
ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, slen, NULL, prv->rbuf, &rlen);
while( ((ret != SCARD_S_SUCCESS) || (rlen < 6)) && (retry_count < 2) ){
retry_count += 1;
// if(!connect_card(prv, prv->reader)){
// continue;
// }
// slen = setup_emm_receive_command(prv->sbuf, src, len);
rlen = B_CAS_BUFFER_MAX;
ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, slen, NULL, prv->rbuf, &rlen);
}
if( (ret != SCARD_S_SUCCESS) || (rlen < 6) ){
return B_CAS_CARD_ERROR_TRANSMIT_FAILED;
}
return 0;
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
private method implementation
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static B_CAS_CARD_PRIVATE_DATA *private_data(void *bcas)
{
B_CAS_CARD_PRIVATE_DATA *r;
B_CAS_CARD *p;
p = (B_CAS_CARD *)bcas;
if(p == NULL){
return NULL;
}
r = (B_CAS_CARD_PRIVATE_DATA *)(p->private_data);
if( ((void *)(r+1)) != ((void *)p) ){
return NULL;
}
return r;
}
static void teardown(B_CAS_CARD_PRIVATE_DATA *prv)
{
if(prv->card != 0){
SCardDisconnect(prv->card, SCARD_LEAVE_CARD);
prv->card = 0;
}
if(prv->mng != 0){
SCardReleaseContext(prv->mng);
prv->mng = 0;
}
if(prv->pool != NULL){
free(prv->pool);
prv->pool = NULL;
}
prv->reader = NULL;
prv->sbuf = NULL;
prv->rbuf = NULL;
prv->id.data = NULL;
prv->id_max = 0;
}
static int change_id_max(B_CAS_CARD_PRIVATE_DATA *prv, int max)
{
intptr_t m;
intptr_t reader_size;
int pwctrl_size;
uint8_t *p;
uint8_t *old_reader;
uint8_t *old_pwctrl;
reader_size = prv->sbuf - prv->pool;
pwctrl_size = prv->pwc.count * sizeof(B_CAS_PWR_ON_CTRL);
m = reader_size;
m += (2*B_CAS_BUFFER_MAX);
m += (max*sizeof(int64_t));
m += (prv->pwc_max*sizeof(B_CAS_PWR_ON_CTRL));
p = (uint8_t *)malloc(m);
if(p == NULL){
return B_CAS_CARD_ERROR_NO_ENOUGH_MEMORY;
}
old_reader = (uint8_t *)(prv->reader);
old_pwctrl = (uint8_t *)(prv->pwc.data);
prv->reader = (LPTSTR)p;
prv->sbuf = prv->pool + reader_size;
prv->rbuf = prv->sbuf + B_CAS_BUFFER_MAX;
prv->id.data = (int64_t *)(prv->rbuf + B_CAS_BUFFER_MAX);
prv->id_max = max;
prv->pwc.data = (B_CAS_PWR_ON_CTRL *)(prv->id.data + prv->id_max);
memcpy(prv->reader, old_reader, reader_size);
memcpy(prv->pwc.data, old_pwctrl, pwctrl_size);
free(prv->pool);
prv->pool = p;
return 0;
}
static int change_pwc_max(B_CAS_CARD_PRIVATE_DATA *prv, int max)
{
intptr_t m;
intptr_t reader_size;
int cardid_size;
uint8_t *p;
uint8_t *old_reader;
uint8_t *old_cardid;
reader_size = prv->sbuf - prv->pool;
cardid_size = prv->id.count * sizeof(int64_t);
m = reader_size;
m += (2*B_CAS_BUFFER_MAX);
m += (prv->id_max*sizeof(int64_t));
m += (max*sizeof(B_CAS_PWR_ON_CTRL));
p = (uint8_t *)malloc(m);
if(p == NULL){
return B_CAS_CARD_ERROR_NO_ENOUGH_MEMORY;
}
old_reader = (uint8_t *)(prv->reader);
old_cardid = (uint8_t *)(prv->id.data);
prv->reader = (LPTSTR)p;
prv->sbuf = prv->pool + reader_size;
prv->rbuf = prv->sbuf + B_CAS_BUFFER_MAX;
prv->id.data = (int64_t *)(prv->rbuf + B_CAS_BUFFER_MAX);
prv->pwc.data = (B_CAS_PWR_ON_CTRL *)(prv->id.data + prv->id_max);
prv->pwc_max = max;
memcpy(prv->reader, old_reader, reader_size);
memcpy(prv->id.data, old_cardid, cardid_size);
free(prv->pool);
prv->pool = p;
return 0;
}
static int connect_card(B_CAS_CARD_PRIVATE_DATA *prv, LPCTSTR reader_name)
{
int m,n;
long ret;
unsigned long rlen,protocol;
uint8_t *p;
if(prv->card != 0){
SCardDisconnect(prv->card, SCARD_RESET_CARD);
prv->card = 0;
}
ret = SCardConnect(prv->mng, reader_name, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T1, &(prv->card), &protocol);
if(ret != SCARD_S_SUCCESS){
return 0;
}
m = sizeof(INITIAL_SETTING_CONDITIONS_CMD);
memcpy(prv->sbuf, INITIAL_SETTING_CONDITIONS_CMD, m);
rlen = B_CAS_BUFFER_MAX;
ret = SCardTransmit(prv->card, SCARD_PCI_T1, prv->sbuf, m, NULL, prv->rbuf, &rlen);
if(ret != SCARD_S_SUCCESS){
return 0;
}
if(rlen < 57){
return 0;
}
p = prv->rbuf;
n = load_be_uint16(p+4);
if(n != 0x2100){ // return code missmatch
return 0;
}
memcpy(prv->stat.system_key, p+16, 32);
memcpy(prv->stat.init_cbc, p+48, 8);
prv->stat.bcas_card_id = load_be_uint48(p+8);
prv->stat.card_status = load_be_uint16(p+2);
prv->stat.ca_system_id = load_be_uint16(p+6);
return 1;
}
static void extract_power_on_ctrl_response(B_CAS_PWR_ON_CTRL *dst, uint8_t *src)
{
int referrence;
int start;
int limit;
dst->broadcaster_group_id = src[8];
referrence = (src[9]<<8)|src[10];
start = referrence - src[11];
limit = start + (src[12]-1);
extract_mjd(&(dst->s_yy), &(dst->s_mm), &(dst->s_dd), start);
extract_mjd(&(dst->l_yy), &(dst->l_mm), &(dst->l_dd), limit);
dst->hold_time = src[13];
dst->network_id = (src[14]<<8)|src[15];
dst->transport_id = (src[16]<<8)|src[17];
}
static void extract_mjd(int *yy, int *mm, int *dd, int mjd)
{
int a1,m1;
int a2,m2;
int a3,m3;
int a4,m4;
int mw;
int dw;
int yw;
mjd -= 51604; // 2000,3/1
if(mjd < 0){
mjd += 0x10000;
}
a1 = mjd / 146097;
m1 = mjd % 146097;
a2 = m1 / 36524;
m2 = m1 - (a2 * 36524);
a3 = m2 / 1461;
m3 = m2 - (a3 * 1461);
a4 = m3 / 365;
if(a4 > 3){
a4 = 3;
}
m4 = m3 - (a4 * 365);
mw = (1071*m4+450) >> 15;
dw = m4 - ((979*mw+16) >> 5);
yw = a1*400 + a2*100 + a3*4 + a4 + 2000;
mw += 3;
if(mw > 12){
mw -= 12;
yw += 1;
}
dw += 1;
*yy = yw;
*mm = mw;
*dd = dw;
}
static int setup_ecm_receive_command(uint8_t *dst, uint8_t *src, int len)
{
int r;
r = sizeof(ECM_RECEIVE_CMD_HEADER);
memcpy(dst+0, ECM_RECEIVE_CMD_HEADER, r);
dst[r] = (uint8_t)(len & 0xff);
r += 1;
memcpy(dst+r, src, len);
r += len;
dst[r] = 0;
r += 1;
return r;
}
static int setup_emm_receive_command(uint8_t *dst, uint8_t *src, int len)
{
int r;
r = sizeof(EMM_RECEIVE_CMD_HEADER);
memcpy(dst+0, EMM_RECEIVE_CMD_HEADER, r);
dst[r] = (uint8_t)(len & 0xff);
r += 1;
memcpy(dst+r, src, len);
r += len;
dst[r] = 0;
r += 1;
return r;
}
static int32_t load_be_uint16(uint8_t *p)
{
return ((p[0]<<8)|p[1]);
}
static int64_t load_be_uint48(uint8_t *p)
{
int i;
int64_t r;
r = p[0];
for(i=1;i<6;i++){
r <<= 8;
r |= p[i];
}
return r;
}