免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 6240 | 回复: 4
打印 上一主题 下一主题

[算法] [原创] Linux下的cryptoloop的使用方法和算法分析 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-10-31 16:10 |只看该作者 |倒序浏览
最近的项目有一个如下的需求:需要在linux下加密优盘,在windows下要能读取优盘中的加密数据。但有个条件:必须对整个磁盘加密,不能只对文件加密(于是复杂度大大增加)。咋办呢?
最后用下面的方法实现了这个需求:对于linux下的优盘加密问题,直接用losetup生成一个加密的loop镜像文件,然后用fat32格式化这个镜像文件,再将整个镜像写入优盘。对于windows上的数据读取,最关键的问题是怎么解密镜像文件。(已解决)

其实网上类似的应用很多,例子也不少,但是有两个缺陷:
1.
封装的不好,使用起来有点烦。
2.
没有对解密算法的分析,像我这个项目需要知道解密loop镜像的算法就比较郁闷。
所以这篇小文就把网上流传的代码做了些补充,做了封装让它更好用,还有就是对加密算法做了分析,最后附上了相关代码,以后别人有类似的应用也可以方便些。

(一)
cryptoloop使用方法:
网上类似的代码太多了,这里不再螯述,我根据网上搜集的资料写了几个脚本,能很容易的实现对加密优盘的读写,使用方法见下文,源代码在后面的附录中。注意,文件镜像用的是fat32格式。
使用方法:
1.
数据复制到优盘:
用法:./data2imagesize(M) password filelist device
Size:是优盘的大小
Password:是优盘加密密码
Device:就是优盘设备名
filelist:一个文本文件,保存了要复制的文件或者目录列表。
例子:./ data2image100 mypassword /home/user001/filelist /dev/sdc1
Filelist的例子:
/home/user001/mytest.txt
/home/user001/mytestdir

2.
优盘中的数据复制到指定文件夹:
用法:./image2datasize(M) password device destination
Size:是优盘的大小
Password:是镜像密码
Device:就是优盘设备名
Destination:目标文件夹
例子:./image2data100 mypassword /dev/sdc1 /tmp/data

使用前要先配置下环境,我的开发环境:RHEL5企业版,expect5.43tcl8.5.4tk8.5.4 expect等三个开发包是为了密码输入方便)。如果用的是RHEL5企业版的话只要装expect,tcltk就行了,否则的话可能还要编译内核让它支持cryptoloop,另外可能还要还要编译安装util-linux-2.12r(该工具包中有losetup)。

(二)
解密算法的代码:
(这是全文最重要的部分但却是最简单的,呵呵 分析了半天发现加密算法竟这么简单,我也有点晕)
对于fat32来说,cryptoloop是按照扇区加密的,也就是每512字节作为一个块加密,并且以扇区号作为ivec参数,aes key则是直接由用户密码生成。解密算法比较简单,我写了段代码来说明linux的解密算法,这样比较直观。具体代码参见附录。

注意:
1.如果在losetup中指定算法的时候指定了aes作为算法(而没有明确指定是cbc还是ecb),则默认使用cbc算法。
2.Aes-cbc全称是Advanced Encryption Standard (AES) Cipher Algorithm in Cipher BlockChaining (CBC) Mode。该算法中还需要指定iv参数,可将其视为salt
3.该算法其他具体信息参见rfc3602
4.代码中用了openssl库,所以要想运行代码必须先安装openssl

(三)算法的分析过程:(没兴趣的请跳过这节:
其实我觉得算法的分析过程才是最重要的,比得出的结论还要重要。我把这个过程写下来,以后遇到类似问题可以参考,也方便其他人查找cryptoloop解密算法。

分析解密算法前首先要查看加密的镜像文件的内容,说不定能找到一些重要的线索呢。于是创建了两个相同大小相同密码的loop镜像文件(为什么要密码相同呢?这是为了检查是否对不同密码使用了salt。另外,为了便于分析,创建了两个1M的镜像),然后比较这两个文件,发现除了前512字节不同外,其他加密数据一模一样,所以得出结论:
1.
有可能是以512字节的扇区作为数据块加密的(猜测1)。(事实上的确如此)
2.
可能aes没有加salt或者所有密码都用相同的salt(猜测2)。(事实上没有加salt

继续,因为是用的cryptoloop模块,所以有必要找到cryptoloop的代码先。在内核代码中搜索loop找到了cryptoloop的源代码:linux-2.6.26.1\drivers\block\cryptoloop.c

再继续,因为加密模块是aes,所以在内核源代码目录中搜索aes,找到了一大堆aes加密算法,不知道是哪个。最后确定是linux-2.6.26.1\linux-2.6.26.1\drivers\crypto\padlock-aes.c
为什么呢?因为文件最后的模块别名清楚的写着:MODULE_ALIAS("aes"); 所以这就是传说中的aes模块。

但是这个模块里又有cbc_aes_algecb_aes_alg两种加密算法,cryptoloop用的是哪种算法呢?是cbc_aes_alg算法。
为什么呢?请看cryptoloop.c中的cryptoloop_init函数,当中有段代码:

if(!mode_len) {

mode= "cbc";

mode_len= 3;

}
就是在用户没有指定具体算法的时候使用cbc-aes算法(猜测3。好,具体算法也被确定了。

本来是想好好分析下cbc(aes)加密算法的,但是内核中的调用实在太复杂,层次又多,看了一上午,无语了

后来转念一想,干脆试试openssl现成的cbc(aes)算法吧,查了一下:opensslAES_cbc_encrypt函数除了两个重要参数没法确定外,另外几个参数都很容易确定。这两个参数一个是ivec参数,另外一个就是key了。

先确定key参数。我知道key肯定是由losetup创建设备时输入的密码生成的,所以干脆下载了一份losetup代码来分析(顺便说一句,losetup的代码包含在util-linux-2.12r开发包中,可在kernel.org上下载)。呵呵,这个思路是对的,我从用户输入的密码开始跟踪,发现密码传递的路径如下:
密码被复制进了loop_info64lo_encrypt_key(注意:lo_encrypt_key的长度是32字节)中,然后在内核源代码目录中搜索lo_encrypt_key关键字(用grep搜索),发现cryptoloop.c中的函数cryptoloop_init调用了crypto_blkcipher_setkey并且以lo_encrypt_key为参数,由此确定key是由lo_encrypt_key生成的,换句话说就是直接用用户的密码生成key(猜测4。(又顺便查看了padlock-aes.c的生成aeskey的函数,也就是aes_set_key,发现果然生成过程中没有用salt,这也正印证了前面分析镜像文件时的猜测2

另一个参数ivec采用类似的方法从padlock-aes.ccbc_aes_decrypt一层层倒推,最后发现是cryptoloop.c中的函数cryptoloop_transfer用扇区号生成的(猜测5,具体过程如下:
cbc_aes_decrypt实际上是调用padlock_xcrypt_cbc函数解密的,而padlock_xcrypt_cbc也有一个iv参数,我根据这个参数一层层倒推(就是根据函数栈的调用顺序),发现cryptoloop_transfer中有如下代码


u32 iv[4] = { 0, };

iv[0]= cpu_to_le32(IV & 0xffffffff); //这里的IV就是扇区号
好,iv参数也被确定了。

如此根据以上五个猜测结论写了一个小程序模拟内核加密解密,竟然一次成功!
呵呵,整个分析过程都是建立在推理和猜测的基础上,其实读源代码就是这样,要大胆猜测小心论证!还要有一点狗屎运!

注:
分析源代码的过程中还发现了下面这个函数,看了晕不晕?呵呵,网上查了下原来是在调用硬件版本的解密函数,0xf3,0x0f,0xa7,0xd0就是cpu指令repxcryptcbc,后面的SD之类的东东是寄存器。
static inline u8 *padlock_xcrypt_cbc(constu8 *input, u8 *output, void *key,


u8 *iv, void *control_word, u32 count)
{

/*rep xcryptcbc */

asmvolatile (".byte 0xf3,0x0f,0xa7,0xd0"


: "+S" (input), "+D"(output), "+a" (iv)


: "d" (control_word),"b" (key), "c" (count));

returniv;
}

(四)附录:
一共有五个文件,前四个文件是读写优盘的脚本,最后一个文件是解密程序。
1.
linkwrapper
#!/usr/local/bin/expect
set password [lindex $argv 0]

spawn losetup -e aes /dev/loop0/tmp/cryptoloop.image

expect {

Password: {

send "$password\r"

exp_continue

}
}

2.
mountwrapper
#!/usr/local/bin/expect
set password [lindex $argv 0]

spawn mount -t vfat /tmp/cryptoloop.image/mnt/crypto/ -oencryption=aes

expect {

Password: {

send "$password\r"

exp_continue

}
}

3.
data2image
#!/bin/sh
#The script is used to copy files todevice.

if [ $# != 4 ]
then

echo"Usage: $0 size(M) password filelist device"

exit1;
fi

DISKSIZE=$1
PASSWORD=$2
FILELIST=$3
DEVICE=$4

echo "checking environment..."
modprobe aes > /dev/null 2>&1
if [ $? != 0 ]
then

echo"Errors checking aes module" >&2

exit1
fi

modprobe cryptoloop > /dev/null2>&1
if [ $? != 0 ]
then

echo"Errors checking cryptoloop module" >&2

exit1
fi

echo "initializing cryptoloopimage..."
umount /mnt/crypto > /dev/null2>&1
losetup -d /dev/loop0 > /dev/null2>&1
rm -f /tmp/cryptoloop.image > /dev/null2>&1
dd if=/dev/zero of=/tmp/cryptoloop.imagebs=1M count=$DISKSIZE > /dev/null 2>&1
if [ $? != 0 ]
then

echo"Errors initializing cryptoloop image" >&2

exit1
fi

echo "linking image to device/dev/loop0..."
./linkwrapper $PASSWORD > /dev/null2>&1
if [ $? != 0 ]
then

echo"Errors linking image to device /dev/loop0" >&2

exit1
fi

echo "formatting device/dev/loop0..."
mkfs -t vfat /dev/loop0 > /dev/null2>&1
if [ $? != 0 ]
then

echo"Errors formatting device /dev/loop0" >&2

exit1
fi

mkdir /mnt/crypto > /dev/null2>&1

echo "mounting device/dev/loop0..."
./mountwrapper $PASSWORD > /dev/null2>&1
if [ $? != 0 ]
then

echo"Errors mounting device /dev/loop0" >&2

exit1
fi

#do something here
cat $FILELIST | \
while read line;do

mkdir -p /mnt/crypto/data$line > /dev/null2>&1

/bin/cp -dpR $line /mnt/crypto/data$line >/dev/null 2>&1
done

#clear
umount /mnt/crypto > /dev/null2>&1
losetup -d /dev/loop0 > /dev/null2>&1

echo "copying files to $DEVICE..."
dd if=/tmp/cryptoloop.image of=$DEVICEbs=1M count=$DISKSIZE > /dev/null 2>&1
if [ $? != 0 ]
then

echo"Errors copying files to $DEVICE" >&2

exit1
fi

#delete the image file
rm -f /tmp/cryptoloop.image

exit 0

4.
image2data
#!/bin/sh
#The script is used to copy files fromdevice.

if [ $# != 4 ]
then

echo"Usage: $0 size(M) password device destination"

exit1;
fi

DISKSIZE=$1
PASSWORD=$2
DEVICE=$3
DESTINATION=$4

echo "checking environment..."
modprobe aes > /dev/null 2>&1
if [ $? != 0 ]
then

echo"Errors checking aes module" >&2

exit1
fi

modprobe cryptoloop > /dev/null2>&1
if [ $? != 0 ]
then

echo"Errors checking cryptoloop module" >&2

exit1
fi

echo "initializing cryptoloopimage..."
umount /mnt/crypto > /dev/null2>&1
losetup -d /dev/loop0 > /dev/null2>&1
rm -f /tmp/cryptoloop.image > /dev/null2>&1
dd if=$DEVICE of=/tmp/cryptoloop.imagebs=1M count=$DISKSIZE > /dev/null 2>&1
if [ $? != 0 ]
then

echo"Errors initializing cryptoloop image" >&2

exit1
fi

echo "linking image to device/dev/loop0..."
./linkwrapper $PASSWORD > /dev/null2>&1
if [ $? != 0 ]
then

echo"Errors linking image to device /dev/loop0" >&2

exit1
fi

mkdir /mnt/crypto > /dev/null2>&1

echo "mounting device /dev/loop0..."
./mountwrapper $PASSWORD > /dev/null2>&1
if [ $? != 0 ]
then

echo"Errors mounting device /dev/loop0" >&2

exit1
fi

#do something here
echo "copying files from image..."
cp -dpR /mnt/crypto/data $DESTINATION >/dev/null 2>&1

#clear
umount /mnt/crypto > /dev/null2>&1
losetup -d /dev/loop0 > /dev/null2>&1
rm -f /tmp/cryptoloop.image
dd if=/dev/zero of=$DEVICE bs=1Mcount=$DISKSIZE > /dev/null 2>&1

exit 0

5.
decrypt.c
#include <stdlib.h>
#include <stdio.h>

#include <openssl/rand.h>
#include <openssl/sha.h>
#include <openssl/aes.h>

int decrypt( const char * password, constchar * srcpath, const char * dstpath )
{

charkeybuf[32];

AES_KEY key;

unsigned char blank[512];

unsigned char endata[512];

FILE* psrcf = NULL;

FILE* pdstf = NULL;

longsrcfilelen = 0;

intres = 0;

inti=0;



//因为生成密钥的userkey256位所以不能超过32个字符

if (strlen( password ) > 32 )

return 1;



memset( keybuf, '\0', 32 );

strncpy( keybuf, password, min( 32, strlen(password ) ) );



memset( blank, '\0', 512 );



memset(endata, '\0', 512 );


psrcf = fopen( srcpath, "rb" );

pdstf = fopen( dstpath, "wb" );


fseek( psrcf, 0, SEEK_END );

srcfilelen = ftell( psrcf );

if(srcfilelen%512 != 0 ) {

res= 1;

goto end;

}



//使用aes256算法

AES_set_decrypt_key( (const unsigned char*)keybuf, 256, &key );



fseek( psrcf, 0, SEEK_SET );

for(i=0; ; ++i ) {

//假设镜像文件的长度必须是512的整数倍,如果是不是的话就算出错

intcount = fread( endata, 1, 512, psrcf );

if(count != 512 || feof( psrcf ) ) {

res = 1;

goto end;

}


//如果读到的加密扇区全部为0的话,说明这个扇区没有加密

if(memcmp(endata,blank,512)!=0 ) {

long iv[4];

memset( iv, '\0', 16 );

iv[0] = i;//扇区号被用作iv



//解密

AES_cbc_encrypt((const unsigned char*)endata,endata,512,&key,(unsigned char *)iv,AES_DECRYPT);

}



fwrite(endata,1,512,pdstf);

}


end:

fclose( psrcf );

fclose( pdstf );

return0;
}

int main(int argc, char* argv[])
{

decrypt ( argv[3], argv[1], argv[2] );

return0;
}

[ 本帖最后由 tassard 于 2008-10-31 16:16 编辑 ]

imageandfile.rar

2.54 KB, 下载次数: 104

附录中的代码

论坛徽章:
0
2 [报告]
发表于 2008-10-31 16:30 |只看该作者
不错

论坛徽章:
0
3 [报告]
发表于 2008-10-31 16:38 |只看该作者
支持原创,看的一个累啊,整理一下搞成论文发表算了

论坛徽章:
36
IT运维版块每日发帖之星
日期:2016-04-10 06:20:00IT运维版块每日发帖之星
日期:2016-04-16 06:20:0015-16赛季CBA联赛之广东
日期:2016-04-16 19:59:32IT运维版块每日发帖之星
日期:2016-04-18 06:20:00IT运维版块每日发帖之星
日期:2016-04-19 06:20:00每日论坛发贴之星
日期:2016-04-19 06:20:00IT运维版块每日发帖之星
日期:2016-04-25 06:20:00IT运维版块每日发帖之星
日期:2016-05-06 06:20:00IT运维版块每日发帖之星
日期:2016-05-08 06:20:00IT运维版块每日发帖之星
日期:2016-05-13 06:20:00IT运维版块每日发帖之星
日期:2016-05-28 06:20:00每日论坛发贴之星
日期:2016-05-28 06:20:00
4 [报告]
发表于 2008-10-31 16:50 |只看该作者
赞一个。

论坛徽章:
0
5 [报告]
发表于 2008-10-31 17:07 |只看该作者
支持原创
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP