包装信息可能包含:指定目标设备、代码版本、大小、日期和其它对用户有用的信息。这个信息可警告操作员正在使用一个较低版本的固件,从而会使设备的部分性能降低,或者正在装载一个不支持的指定设备。
在如今竞争激烈的市场中,保护公司的知识产权(IP)是最高优先级的事项之一。多年的开发可能产生了包含很多商业秘密或专有算法的设计。随着基于闪存的微控制器得到更多地应用并集成了更多的特性,许多公司摒弃传统的一次性可编程(OTP)微控制器(MCU),转而采用闪存。
闪存允许在生产线的终点进行实时编程,也允许在生产之后修改代码。但是如果通过因特网,公司将面临IP将泄漏给竞争者的问题。应该采用各种方法来控制更新授权许可,并在泄漏产品的IP的情况下, 提供现场更新。
代码保护
保护现场配置的软件产品的方法很多,采用哪种方法取决于供应商希望保护什么。大多数软件保护机制是防止对程序或数据进行未经授权的复制。如今复制文件非常容易,通过因特网能很快地找到许多拷贝。当固件是某个硬件平台的一部分时,保护固件中包含的IP通常比保护对文件的拷贝更重要。当然,如果固件更新需要购买,那么保护拷贝也很重要。
另一个问题涉及基于微控制器产品破解密码的性能。RSA和DES标准对性能和存储器的要求都很高。不幸的是,大多数的微控制器在这两方面的资源都有限,从而要求不同的加密和解密方法。我们必须提供一种通用的或者针对特定设备的密钥方案。
简单密码
循环码(Rolling-code)产生器已在车库门锁、安全进出卡等多种设备上应用多年。图1显示简单的循环码产生器是如何工作的。一组具有相同时钟信号的双稳态触发器串联连接,其输出端有各种抽头。在给出的例子中,Q2端没有被使用,其余的输出端连接唯一的OR功能端口,该端口在每个时钟信号沿得到新的输入位。可能产生的组合数目取决于触发器的个数和接到OR函数端口的抽头个数。
通常将几个触发器的输出用作循环钥匙码。实际应用时(如作在车库门锁),使用一个完全相同的发生器来测试钥匙码。随着新码被不断地使用,会发生整个序列重复出现的情况,这可能是在千万或几亿个钥匙码之后。这时就不能再使用它了。在用于车库门锁的情况下,它可以防止罪犯用无线接收机盗听并简单地重现钥匙码以开门。
在代码加密的情况下,触发器的输出与被加密或解密的数据被送到另外一个OR函数端口(图2中的A1)。数据的每个字节、字或双字都与选中的触发器端口进行OR运算,再加上用户特定另外函数。那个函数可以简单到只加上一个常数。这个过程必须是可逆的,所以用户定义的函数不是随机的。由安置触发器抽头决定的密码和由移位寄存器的初态、所选定的种子一起提供了加密和解密的算法。可按照串行数字的方法拆分这些密码,以向特定设备提供唯一的密码。也就是说,设备的串行号码仅提供一半的密码信息,制造商保留其余密码信息。很明显,这种方案存在着多种排列。
在固件更新是向全球发布时,例如,适用所有产品的错误的更新,可以使用一个特别的串行码。解密引擎首先尝试用全球码,-----可能是一个特殊数字图样。如果全球码没有解密出正确的校验和,才使用唯一的密码。它可以告诉装载程序:加密的源文件包含了什么类型的固件更新数据。
传输方法
很明显,产品内必须要有某种解码引擎和代码装载程序。这里讨论的技术可以用COP8微控制器来解释。该目标控制器是包含32KB闪存和1KB RAM的COP8CBR。 对于现场可更新的设备,必须注意这样一个说明以防止误解:微控制器必须具有一边运行程序一边对闪存进行编程的能力。不能用把控制器保持在重新设置状态或者要求外部编程来更新存储着程序的闪存。解码和装载程序必须驻留在控制器内部,并且完全可靠。COP8闪存产品能满足这些要求。
固件的装载程序是一个代码模块,它在重新设置后运行,并能判断本次装载是否有效。如果发现被装载的代码无效,装载程序就进入装载模式(LOAD MODE),直到有能够操作的固体程序。可通过控制器提供的物理层接口来实现这点。COP8可使用串口。如果当前代码是有效的,装载程序会把控制交给固件应用程序的入口。
COP8系列的另一个特性是可以采用软件陷阱以引导恢复机制。它执行操作码0x00,可将这个操作码编程到所有未用单元。如果这个操作码被执行,操作将从软件陷阱向量开始,使装载程序重新评估固件的完整性。这是一个容错的好特性。不管什么原因,代码被破坏,装载程序将得到控制,以便让技术人员(或用户)重新装载固件。
装载程序使用数据包,数据包有数据头和校验和以验证传输。如果数据包传输到了控制器并且校验和是正确的,则装载程序把它交给解码引擎(如果它是加密的)。在加密数据的内部有另外一个密码校验和,这给解密引擎一个验证密码信息的手段。它将首先应用通用码并测试校验和,如果失败,则利用来自串行数字的唯一密码重试。如果再次失败,则告诉装载程序固件对这个设备无效。这也可防止代码被未经授权的篡改和意外损坏。
如果解密引擎成功地解码数据, 它将把数据交给在闪存中对实际闪存块进行编程的程序例程。当然,对于不同产家,这个程序是不同的,在后面的设计实例中将给出COP8的程序。这个程序保留了一个128字节的RAM用于更新闪存。当然,这块RAM在装载程序完成后可以存放应用程序的数据。
设计实例
图3是一个具备解密功能的固件装载程序的流程图。装载程序使用在编程器和系统模拟程序中广泛使用的修改版Intel HEX数据格式。这将使应用程序现有架构支持标准并易于使用。装载固件程序用COP8汇编语言编写,以简少代码,并利用器件全部的闪存特性。
简单来说,Intel HEX格式是一种用ASCII编码表示的数据记录格式,用于向设备的存储器编程对象代码或数据。该格式由下列字段组成:使用ASCII字符0x3A或者即“:”记录起始字段、长度范围为0x00-0xFF的数据长度字段(两个ASCII字符)、偏移字段、记录类型字段、信息或数据字段以及校验和字段。每个字段都由标准的可打印ASCII字符表示。例如,“10”代表十六进制0x10或者十进制16,可表示长度字段。实际上它的编码是0x31、0x30,这是“0” 和“1” 的ASCII码。因为内容是可打印的ASCII字符,能被文本编辑器查看,所以需要加密。
表里包括了标准的Intel HEX格式记录类型。注意该标准只定义了6种记录类型。为利用现有的结构,装载程序采用Intel HEX 格式,并指定加密数据为一种新的记录类型。记录类型0x10将被用于代表加密数据,这是不能变的。
代码装载程序也需要一个流程控制方法,以保证在下一个记录装载之前,写闪存的周期已经完成。在大多数的情况下,应用程序会产生可分成16个字节一组的文件,以方使阅读。通常闪存是以128或256字节进行分块,因此必须有一个装载程序的命令要求从主机来的数据允许可变记录大小。装载程序必须能处理256字节的数据,因为按照标准,这是最大的记录大小。表的底部显示了本例中新扩展的记录类型。因为COP8系列器件只有16位地址,无法执行记录类型0x02到0x05。对于具备更大闪存的处理器,例如CR16,它能直接访问16M字节的内存,就需要有扩展线性地址的记录类型。
观察图3中的流程图,重新设置后的第一个任务是检验闪存的内容。这是通过读取各个128字节的页面(除了位于闪存顶部的自举验证代码和装载程序代码)来完成的。只验证应用程序的代码这使计算校验和非常容易。但如果需要,它也可以包括检测装载程序部件。当通过校验和检查,控制就被向量转移到应用程序固件的入口。入口保持不变非常重要。如果它发生了移动,装载程序会把向量转到一个不正确的入口,并可能出现崩溃。另外,装载程序可以从代码中读出进入向量,把向量压入堆栈并返回,以迫使从那个地址开始执行。这个方案允许入口点可变。
如果校检和失败,装载程序将保持控制并不断地往主机发送信号,用硬件设计实现的任何手段开始装载。装载程序也可以控制用户接口通知这种状态。在调试系统时,一个表示“固件装载” 的简单的LED灯非常有用。
装载程序将持续地向主机请求记录,直到主机通过EOF记录表明没有记录了。检测到的0x10类型记录 将被送到解码引擎,并适当解码数据。这使得实际向闪存写数据的代码,既可以操作加密数据,也可操作对未加密数据。对开发和内部测试来说,未加密的数据通路很有价值。当收到EOF记录后,所有新固件数据都被写入闪存。然后,装载程序把控制交还给自举测试,以验证新代码未被破坏。
如上所述,现场可更新设备可以在不曝露IP的情况下得到实现。使用一个简单的密码, 制造商的固件就能得到保护,客户可以在世界各地能够访问因特网的地方更新他们的设备。