linux内核支持loop,[原创] Linux下的cryptoloop的使用方法和算法分析

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

(2012-04-10 04:50:38)

标签:

算法

原创

linux

杂谈

[原创]

Linux下的cryptoloop的使用方法和算法分析最近的项目有一个如下的需求:需要在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.43,tcl8.5.4,tk8.5.4

(expect等三个开发包是为了密码输入方便)。如果用的是RHEL5企业版的话只要装expect,tcl和tk就行了,否则的话可能还要编译内核让它支持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_alg和ecb_aes_alg两种加密算法,cryptoloop用的是哪种算法呢?是cbc_aes_alg算法。

为什么呢?请看cryptoloop.c中的cryptoloop_init函数,当中有段代码:

if(!mode_len) {

mode= "cbc";

mode_len= 3;

}

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

本来是想好好分析下cbc(aes)加密算法的,但是内核中的调用实在太复杂,层次又多,看了一上午,无语了 囧iptables转发到回环地址的问题

r />

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

先确定key参数。我知道key肯定是由losetup创建设备时输入的密码生成的,所以干脆下载了一份losetup代码来分析(顺便说一句,losetup的代码包含在util-linux-2.12r开发包中,可在kernel.org上下载)。呵呵,这个思路是对的,我从用户输入的密码开始跟踪,发现密码传递的路径如下:

密码被复制进了loop_info64的lo_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.c的cbc_aes_decrypt一层层倒推,最后发现是由cryptoloop.c中的函数cryptoloop_transfer用扇区号生成的(猜测5),具体过程如下:

cbc_aes_decrypt实际上是调用padlock_xcrypt_cbc函数解密的,而padlock_xcrypt_cbc也有一个iv参数,我根据这个参数一层层倒推(就是根据函数栈的调用顺序),发现cryptoloop_transfer中有如下代码

u32 iv = { 0http://www.faminorson.com, };

iv= cpu_to_le32(IV & 0xffffffff);

//这里的IV就是扇区号

好,iv参数也被确定了。

如此根据以上五个猜测结论写了一个小程序模拟内核加密解密,竟然一次成功!

呵呵,整个分析过程都是建立在推理和猜测的基础上,其实读源代码就是这样,要大胆猜测小心论证!还要有一点狗屎运!

注:

分析源代码的过程中还发现了下面这个函数,看了晕不晕?呵呵,网上查了下原来是在调用硬件版本的解密函数,0xf3,0x0f,0xa7,0xd0就是cpu指令repxcryptcbc,后面的S,D之类的东东是寄存器。

static inline u8 *padlock_xcrypt_cbc(constu8 *input, u8 *output,

void *key,

u8 *iv, void *control_word, u32 count)

{

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

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

expect {

Password: {

send "$password\r"

exp_continue

}

}

2.

mountwrapper

#!/usr/local/bin/expect

set password

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

#include

#include

#include

#include

int decrypt( const char * passwordhttp://www.infooi.com/, constchar

* srcpath, const char * dstpath )

{

charkeybuf;

AES_KEY key;

unsigned char blank;

unsigned char endata;

FILE* psrcf = NULL;

FILE* pdstf = NULL;

longsrcfilelen = 0;

intres = 0;

inti=0;

//因为生成密钥的userkey是256位所以不能超过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(srcfilelenQ2 != 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;

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

iv = 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, argv, argv );

return0;

}

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

]不错支持原创,看的一个累啊,整理一下搞成论文发表算了:mrgreen:赞一个。支持原创:mrgreen:

分享:

a4c26d1e5885305701be709a3d33442f.png喜欢

0

a4c26d1e5885305701be709a3d33442f.png赠金笔

加载中,请稍候......

评论加载中,请稍候...

发评论

登录名: 密码: 找回密码 注册记住登录状态

昵   称:

评论并转载此博文

a4c26d1e5885305701be709a3d33442f.png

发评论

以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值