引言
CAS(Conditional Access System,条件接收系统)是一种加密系统方法总称,通过这个系统可以实现网络控制、收费、加密、管理等诸多功能,广泛应用于视频点播、电子银行、网上超市、远程教育等诸多环境。其与数字广播系统独立,但对商业的数字广播系统来说,是其成功运营的基础、增值服务的命脉,CAS系统须保证未授权用户不能收看加密节目,而授权的用户可以收看加密节目,同时可以对单用户进行识别、控制,消除传统网路电视的盲目性,是我国数字网络改造的核心部分。CAS系统由两部分组成:前端加扰管理系统和终端解扰子系统。机顶盒属于终端设备,本文介绍的CAS系统属于终端解扰子系统。
任何一款复杂电子设备,都离不开一款强大的操作系统支持,而 Android系统起初是Google公司为移动设备开发的一套操作系统,但由于其开源、拥有OHA联盟强大的后盾,而且具有独特的系统架构以及Linux内核底层的稳定支撑,很快有许多其他产品厂商都纷纷开发出 And roid平台,使其成为目前在大型嵌入式设备上最流行的操作系统。目前华为、中必等为代表的机顶盒厂商都在纷纷推出基于Android平台的机顶盒。本文详细介绍了机顶盒CAS终端子系统的设计过程和移植到Android平台上的过程。
1 终端解扰子系统解扰过程
数字电视节目都是通过介质(地面、电缆、卫星)以TS流的形式传播的。TS流依据MPEG-2协议被分成长度188字节的ES包,每一个ES包都有自身识别的PID号,根据PID号的不同分成各种功能不同的表,其中对CAS系统有用的是ECM表和EMM表。EMM(Entitle Manager Message)表为授权管理信息表,里面主要包含每个用户授权的节目数和对应的SK(Service Key)业务密钥信息。ECM(Entitle Control Message)表为授权控制信息表,里面含有对解扰最重要的CW(Control Word)控制字,取得对应节目的CW之后,就可以交给安全模块解扰,解扰后的明文CW就可以用来解扰加密节目,整个解密过程也就完成了。
CA解扰的过程如图1所示。
2 终端子系统设计
考虑到应用设计与底层硬件智能卡进行交互,且Android系统提供有NDK套件工具,使得底层的其他语言的API与Android应用层JAVA语言无缝对接,所以可以把CAS以库的形式存放到Android中间层,供Android上层GUI调用。CAS系统的设计用到上述的ECM表和EMM表,但EMM表和ECM表要从PSI中的PMT、CAT表获得其PID号。图2、图3是用专用工具截取的一段PMT和CAT表里面的CA信息。
从图2可以看出此TS流中的CAT表中含有ca_descriptor描述符,并可以得出ECM的ca_pid为0x0562。而从图3中可以看出,PMT表中含有的EMM表的ca_pid为0xoffe。此时就可以设置操作demod来分配filter通道,过滤出EMM、ECM表的section_descriptor_table,来取得CA有关的信息。如果用户要流畅地播放节目,机顶盒(Set_Top Box,STB)就要不断地获得密文CW送入智能卡中,从整个解码过程中可以把整个CAS终端子系统分为3个模块:EMM解析模块、ECM解析模块、智能卡任务模块。Android系统采用的是Linux内核,保留了posix的pthread、message、memory pool等通用的API,所以在设计3个模块时,可以使用pthread_create()创建任务模块;使用msgget()创建消息,实现3个任务模块的通信与同步。
2.1 EMM任务模块
由图1可知,TS流经tuner调谐,把高频载波去掉,再经过demod解调,就可以根据PES包的PID号和TABLEID号设置其里面的filter。一般来说一个demod含有多个filter,在系统启动开始就会分配PAT表的filter,有PAT表的setction descriptor的描述就可以得到PMT表的PID,此时同样分配PMT表的filter。如果此节目是加密节目,在其段描述符中就会含有EMM表的PID号,一旦找到EMM的PID号,就可以为EMM表分配filter。如果EMM里面的CA信息版本号和智能卡存储的CA信息版本号一致,就舍弃此EMM;如果不一致,就重新改写智能卡里面的CA用户的信息。EMM任务模块流程如图4所示。
在EMM任务模块中,通过CAS_EMM_TASK()函数创建任务,在CAS_EMM_TASK()中调用CAS_EMM_ReceiveMessage()函数来接收EMM filter发送过来的CA信息。在没有EMM流时,EMM任务一直挂起,而一旦filter发现EMM表,就会把EMM的CA信息发送到EMM任务中,同时关掉filter,避免其未处理完此EMM,而又接收新的EMM。此时EMM任务从挂起进入就绪态,从而处理来自filter的EMM信息,处理完之后再次分配EMM的filter。
2.2 ECM任务模块
通过解渎CAT表可知,此节目表是否加密,如果加密则设置相应ECM的filter过滤出对应的ECM表,此时结合智能卡中存储的EMM的CA信息,就可以判断出用户是否对此节目授权,若授权则取出智能卡中的SK业务密钥,找出对应的奇偶控制字(CW),送入到智能卡中,通过智能卡系统解密出CW,送入到STB中实现数据、视频、音频的解码。整个过程如图5所示。
在ECM任务模块中,通过CAS_ECM_TASK()创建线程任务,在其内部调用CAS_ECM_ReceiveMessage()函数接收来自ECM的filter过滤出的CA信息;此时通过CAS_CARD_ReadMessage()读取智能卡内部用户授权信息,来判断ECM是否有效且取出对应的密文的CW;而用CAS_ECM_Send To Card()函数把密文CW送入到智能卡中解密,解密出明文CW;用CAS_CW_SendToSTB()函数送入到机顶盒,此时解复用模块接收到明文CW就可以得到解码加密流了。
2.3 智能卡任务模块
智能卡的通信标准有T0和T1两种,T0按字节传送,T1按块传送,而在设计过程中通常支持两种协议。一般采用I2C总线通信,而智能卡内部一般没有上拉电阻,所以在电路设计过程中,SCL和SDA的引脚处必须加上拉电阻,否则无法正常通信。根据通信协议,如果要对智能卡数据读写操作,首先要发送5字节的命令字,这5字节命令字依次为CLA、INS、P1、P2、P3,其中CLA为指令类型,INS为命令符,P1、P2为操作文件位置,P3为后续字符数。智能卡接收到命令符就可以根据命令种类对其后续数据进行操作,同时智能卡就可以发出两个字节W1、W2的应答符。如果成功,W1、W2分别为0x90、0x00;如果不成功则会返回相应的代码,以便给开发者提供调试。因为智能卡内部十分复杂,篇幅有限,所以想深入了解原理的话可以参考智能卡标准,这里仅介绍机顶盒操作智能卡过程的设计。
如图6所示,在智能卡任务模块中,在系统启动之初,未进入文件系统之前,就要对智能卡进行初始化,分配内存池,强制为智能卡复位,从而选择通信类型(T0或T1),全部完成之后就可以进入文件系统。通过CAS_CARD_TASK()为智能卡建立线程,在其线程内部使用CAS_CARD _ReceiveMessage()接收来自EMM或者ECM的命令字。如果合法,通过CAS_CARD_SendMessage()可以把应答字给其两个模块,同时通知其他两个模块发送操作数,若是EMM则到此结束,若为ECM则智能卡会把解密的CW通过CAS_CARD_SendMessage()发送给机顶盒。
2.4 其他细节设计
CAS系统除了最重要的解扰以外,还有其他重要的附属功能,如邮件、在线付费、在线充值、节目点播、区域限制、用户管理。这些信息都存储在EMM表中,所以EMM和ECM表的解析也是一个十分重要的步骤,只有正确地提取出 EMM中的CA信息,才能顺利地进行下一步的操作。根据MPEG-2标准和PSI/SI协议,以及智能卡厂商的提供功能表,就能设计出EMM和ECM的解析函数。
表1列出了一个通用CA的描述符。
由于每个智能卡厂商的填充数据不一样,所以必须根据厂商的定义再去提取数据、处理数据。由于笔者参与设计的是某公司提供的智能卡,所以数据的格式也都以它为标准。最终设计包括12个源文件、5个头文件。
3 CAS子系统Android的移植
CAS终端子系统起初没计由于涉及到与底层交互,采用的是C语言。如果想要使上层的JAVA环境调用其API,就要遵循JNI规范添加新的头文件,使其应用层能够方便地调用。同时Google在设计Androld之初就提供了NDK套件,有着独有的交叉编译器,使得原有的许多C语言编写的驱动、应用程序可十分方便地移植到Android系统中。
3.1 搭建Android的NDK开发环境
由于是在Windows下进行开发,所以要在Windows下模拟Linux的开发环境,需要下载cygwin工具,下载地址为http://www.cygw in.com/setup.exe。安装方法请参考相关文档,这里就不赘述了。同样也需要Android的NDK套件,下载地址为http://developer.an droid.com/sdk/ndk/index.html;可以选择最新的版本下载,下载完毕,直接解压到同一路径下。然后在cygwin的安装目录home/Adm inistrator下的./bash_profile文件添加NDK的路径,就可以使用NDK下的ndk-build命令了,进入samples/hello-jni。在cygwin中调用ndk-build,如果出现如图7所示的结果,则NDK的环境已经搭建成功。
3.2 编写CAS子系统的makefile
笔者使用的是android-ndk-r7b版本,也是目前最新版本,其交叉编译器位于其toolchains/arm-linux-an-droideabi-4.4.3/pre built/windows/arm-linux-android/bin中,库的头文件位于/platforms/android-xx/arch-arm/usr/include中,库位于platform /android-xx/arch-arm/usr/lib中。知道了编译器和C库的头文件,就可以容易地编写出 makefile。在编写makefile时需要注意,若用到了posix的pthread库,则需要添加“LDFLAGS+=-lpthread”,否则在执行链接的时候会出现错误。编译完成之后如图8所示。
3.3 实现CAS子系统的JNI接口函数
因为CAS子系统提供给外部使用的API达20多个,这里以CASTB_GetVersion()函数为例,其他都是如此实现。新建一个文件夹,命名为STBCA,在文件下建立两个文件夹分别命名为JNI和SRC。JNI存放为CAS的JNI本地API,源文件为castb_api_jni.c;SRC存放的是上层JAVA应用程序,根据JNI标准则需把CASTB_GetVersion()定义为“Java_com_jpf_stbca_STBCA_CASTB_GetVersion();”。只要调用3.2小节的中liBCAS.a库中的源函数就实现了对原函数的包装,在同一目录下添加android.mk,内容如下所示:
LOCAL PATH=$(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE=casjni
LOCAL_SRC FILES=castb_api_jni.c
LOCAL_LDLIBS+=-lcas\
-lpthread
include $(BUILD_SHARED LIBRARY)
通过3.1小节的步骤就可以生成cas_jni.so库,上层如果曼调用cas_jni.so库中的函数只要在JAVA文件中声明public native CASTB _GetVersion()函数,且使用“static{system.loadlibrary(“cas_jni”);}”把动态库加载到连接器中,就完成了全部的设计。通过实践,负责上层软件编写的同时能够无缝地实现CAS系统API的调用。
结语
本文详细阐述了CAS子系统的开发过程和Android系统移植。在Android机顶盒的开发过程中,使用的是华为的H3716C平台,笔者承担了CAS系统和PSI/SI节目表解析的开发与移植。使用此CAS子系统播放加密节目,持续稳定地播放一周而且没有出现马赛克或卡现象,说明此CAS子系统比较稳定。但CAS是一套功能完整的独立系统,而笔者只是重点探讨解密的过程,许多其他功能未有涉及,若想深入了解CAS系统,请参考CAS系统标准。