在物联网产品开发中,经常需要对单片机或其他嵌入式系统进行在线升级,原始二进制文件
在存储和传输的过程中非常不安全,可以对二进制文件进行高效的加密之后再使用,
并在片内进行解密之后再执行各自的升级任务。
比如对整个二进制文件以16字节为段,每段进行AES编码,不足16字节需要补全后再编码,
在新文件尾部使用额外两字节记录与原始文件的差值大小,方便解密还原使用。
binfilecrypto.c
/**
*binary文件高效加解密
*create by iversondeng168
**/
#include <stdio.h>
#include <string.h>
#include "aes.h"
#define I8 char
#define UI8 unsigned char
#define DEBUG
/**
filename:文件路径
mode:加解密模式
*/
static int bin_file_xcode(const I8*filename,UI8 mode)
{
#define NAME_MAX_SIZE (128)
#define DATA_MAX_SIZE (16)
#define MAX_TRYCNT (10)
mbedtls_aes_context context;
FILE * fp = NULL;
FILE * newfp = NULL;
long filelen = 0;
long offset = 0;
long newoffset = 0;
int tmp = 0;
int ret = 0;
int trycnt = 0;
UI8 databuf[DATA_MAX_SIZE];
UI8 outbuf[DATA_MAX_SIZE];
int outbuflen = 0;
I8 newfilename[NAME_MAX_SIZE];
/**
由于AES加解密 16字节对齐问题,
故让加密固件最后面多加两个字节代表与原始固件的大小差值.
*/
UI8 lendiff[2];
long fpfilelen = 0;
int tmplen = 0;
//打开原始文件
fp = fopen(filename, "rb");
if(fp == NULL) return -1;
memset(newfilename,0,sizeof(newfilename));
//根据加解密模式,决定新生成文件名称
switch(mode)
{
case 0:
sprintf(newfilename,"%s-encode",filename);
break;
case 1:
sprintf(newfilename,"%s-decode",filename);
break;
default:
return -1;
}
//打开新生成文件
newfp = fopen(newfilename, "wb+");
if(newfp == NULL) return -1;
//获取原始文件长度
fseek(fp,0,SEEK_END);
filelen = ftell(fp);
fpfilelen = filelen;
#ifdef DEBUG
printf("filelen=%ld\n",filelen);
#endif
//解密模式
if(mode == 1)
{
//读取固件差值
fseek(fp,filelen - sizeof(lendiff),SEEK_SET);
trycnt = 0;
LENDIFF_R_LOOPS:
ret = fread(lendiff, sizeof(lendiff), 1, fp);
if(ret != 1) {
if(trycnt++ < MAX_TRYCNT)
goto LENDIFF_R_LOOPS;
else
return -1;
}
//获取原始文件在加密之前的大小
tmplen = lendiff[0] << 8 | lendiff[1];
filelen -= sizeof(lendiff);
#ifdef DEBUG
printf("tmplen=%d\n",tmplen);
#endif
}
fseek(fp,0,SEEK_SET);
while(filelen > 0)
{
tmp = filelen / DATA_MAX_SIZE ? DATA_MAX_SIZE : filelen;
filelen -= tmp;
trycnt = 0;
//每次读取16字节,不足16字节以剩余为准
FREAD_LOOPS:
ret = fread(databuf, tmp, 1, fp);
if(ret != 1){
if(trycnt++ < MAX_TRYCNT){
#ifdef DEBUG
printf("fread ret=%d\toffset=%d\n",ret,(int)offset);
#endif
goto FREAD_LOOPS;
}
else{
return -1;
}
}
switch(mode)
{
case 0:
//将每次读取的字节进行AES加密
aes_encode(&context,databuf,outbuf,tmp,&outbuflen);
break;
case 1:
//将每次读取的字节进行AES解密
aes_decode(&context,databuf,outbuf,tmp);
if(filelen == 0){
outbuflen = tmp - tmplen;
}
else{
outbuflen = tmp;
}
break;
}
trycnt = 0;
//将每次处理后的字节写入新文件
FWRITE_LOOPS:
ret = fwrite(outbuf, outbuflen, 1, newfp);
if(ret != 1){
if(trycnt++ < MAX_TRYCNT){
#ifdef DEBUG
printf("fwrite ret=%d\tnewoffset=%d\n",ret,(int)newoffset);
#endif
goto FWRITE_LOOPS;
}
else{
return -1;
}
}
//原始文件和新生成文件同步移动文件指针
offset += tmp;
newoffset += outbuflen;
fseek(fp,offset,SEEK_SET);
fseek(newfp,newoffset,SEEK_SET);
}
//加密模式
if(mode == 0)
{
//16字节整数倍
tmplen = fpfilelen % DATA_MAX_SIZE;
//16字节求余数
if(tmplen > 0)
tmplen = DATA_MAX_SIZE - tmplen;
//加密后固件最后两字节代表差数
lendiff[0] = tmplen >> 8;
lendiff[1] = tmplen & 0xFF;
trycnt = 0;
LENDIFF_W_LOOPS:
ret = fwrite(lendiff, sizeof(lendiff), 1, newfp);
if(ret != 1){
if(trycnt++ < MAX_TRYCNT)
goto LENDIFF_W_LOOPS;
else
return -1;
}
}
aes_free(&context);
fclose(fp);
fclose(newfp);
return 0;
}
/**eg.
* binfilecrypto -e demobin #对demobin文件加密
* binfilecrypto -d demobin-encode #对demobin-encode文件解密
*/
int main(int argc,char*argv[])
{
int exemode;
//匹配必须为三个参数
if(argc != 3){
#ifdef DEBUG
printf("argc error!\n");
#endif
return -1;
}
//加密模式
if(!strcmp(argv[1],"-e"))
{
exemode = 0;
}
//解密模式
else if(!strcmp(argv[1],"-d"))
{
exemode = 1;
}
else{
//暂时不支持的模式
#ifdef DEBUG
printf("format not support\n");
#endif
return -1;
}
//加解密统一函数
return bin_file_xcode(argv[2],exemode);
}
交叉编译Makefile文件
CC=/home/openwrt_renew/openwrt-master/staging_dir/toolchain-mips_74kc_gcc-5.3.0_musl-1.1.16/bin/mips-openwrt-linux-gcc
OTHER_HEADERS = aes.h
binfilecrypto:aes_code.o binfilecrypto.o
$(CC) aes_code.o binfilecrypto.o -o binfilecrypto
aes_code.o: $(OTHER_HEADERS)
$(CC) -c aes_code.c
binfilecrypto.o:
$(CC) -c binfilecrypto.c
clean:
rm -rf *.o binfilecrypto