从前面FADE代码解析1可以得知。Client中的main.c主要是通过判断agrc个数不同来做出判断。在进行到process(command)的时候,我们可以看到这个Client主要是将整个框架分为六个部分。主要框架如下所示:
这是主要的流程图,我们可以看到Client主要是由六大块组成的,其中GENSERCT(主要是使用RAND_bytes生成密钥,然后将其写到密钥文件secretFile中去就可以了)以及QUIT比较简单,就不进行详述了。接下来分成UPLOAD、DOWNLOAD、RENEW、REVOKE这四部分进行详述。
在进行讲述函数之前,我先对.metadata文件的相关部署进行一个简要的解释,解释如图所示
1. UPLOAD(fileName,policyName)
{
1.首先FILE *fp = fopen((path + '/' + filename + ".metadata").c_str(), "w");.metadata这个后缀的文件包含了文件的各种属性
2.生成data key=key:RAND_bytes(key, AES_BLOCK_SIZE);//产生一段随机数组作为密钥
3.声明一个k=key:并进行赋值:unsigned char k[AES_BLOCK_SIZE];memcpy(k, key, AES_BLOCK_SIZE);//key作为data key。K要根据policy进行加密 4.生成S key=Ki:RAND_bytes(ki, AES_BLOCK_SIZE);//S key
5.然后以Ki作为参数来形成aes,并对AES_set_encrypt_key(ki, 128, &aes)&&AES_cbc_encrypt(k, kki, AES_BLOCK_SIZE, &aes, iv, AES_ENCRYPT);//加密强度。(char *in,char *out,length,AES_KEY *key,char *ivec,enc).其中AES_ENCRYPT表示加密,其余值表示解密
6.k=[data key]s: memcpy(k, kki, AES_BLOCK_SIZE);
7.将Ki进行分片,并进行策略加密生成新的Ki:ssss_split(AES_BLOCK_SIZE, reinterpret_cast<char *>(ki), reinterpret_cast<char *>(ki_shares), ssss_threshold, ssss_number);
for (int share = 0; share < ssss_number; ++share)// 其中ssss_number是总的片数,ssss_threshold是门槛片数,也就是只要集成的片数>ssss_threshold时,就可以完整的将Ki重现。 { memcpy(ki, ki_shares + AES_BLOCK_SIZE * share, AES_BLOCK_SIZE);//spilt.其中ki参与了[data key]s的加密工作 PolicyForClient policy(subToken, share); //PolicyForClient(policyname,keymanageID) if (!policy.encrypt(ki))//对ki进行policy处理,这样就可以表现出基于策略的加密方式,返回的是ki { fclose(fp); return false; } fwrite(ki, sizeof(unsigned char), rsaSize, fp);//将分片Ki(经过policy加工后的)文件存放到文件中
8.将k=[data key]s 写到文件中:fwrite(k, sizeof(unsigned char), AES_BLOCK_SIZE, fp);//k=[data key]S
9.对filename进行加密:encrypt(filename, key);//key =data key }
2.DOWNLOAD(filename){
1.首先遍历,查看文件是否存在:getFile(filename, ".fade");//遍历看是否存在
2.if (!checkHMAC(filename))//查看文件的HMAC是否正确。,HMAC运算利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出
3.获取文件相关属性:getFile(filename, ".metadata");
4.FILE *fp = fopen((path + '/' + filename + ".metadata").c_str(), "r");打开文件
while (fgets(policyName, POLICY_NAME_LENGTH, fp))//fgets()会加上行尾的/n
{
policyName[strlen(policyName) - 1] = '\0';字符串格式
for (char *str = policyName; ; str = NULL)
{
token = strtok_r(str, sep, &savePtr);//对policyName字符串进行不断地截取
for (int share = 0; share < ssss_number; ++share)//密钥分成多少组? { fread(key, sizeof(unsigned char), rsaSize, fp); PolicyForClient policy(token, share);// PolicyForClient(const string &policyname,int keyManagerID=0) if (share_index < ssss_threshold && policy.decrypt(key))//门槛值,将密钥赋给key.类似于几个组合在一起就可以凑齐key { /* use ssss to combine ki */ memcpy(ki_shares + AES_BLOCK_SIZE * share_index, key, AES_BLOCK_SIZE);//将密钥碎片组合起来 share_indices[share_index++] = share + 1;//position add 1 }
}success = (share_index >= ssss_threshold);// notice if success if (success) { ssss_combine(AES_BLOCK_SIZE, reinterpret_cast<char *>(ki_shares), share_indices, reinterpret_cast<char *>(key), ssss_threshold);//combine one chunk //生成完整的密钥keyKeychain *kc = new Keychain; kc->key = key; kc->next = keychain; keychain = kc;//组成链表
} else { fseek(fp, rsaSize, SEEK_CUR);//重新定位fp内部指针,使得可以从一个新的key来进行下一个policy }
}//根据policyName将加密data key的密钥S解密出来了,其中S被分成几个部分
if (success)//将[data key]s中的S解密出来之后,将S密钥形成环,然后再进行data key解密 { unsigned char kki[AES_BLOCK_SIZE]; fread(kki, sizeof(unsigned char), AES_BLOCK_SIZE, fp); while (keychain) { AES_KEY aes; unsigned char iv[AES_BLOCK_SIZE]; memset(iv, 0, sizeof(iv)); //进行解密data key操作 AES_set_decrypt_key(keychain->key, 128, &aes) unsigned char k[AES_BLOCK_SIZE]; AES_cbc_encrypt(kki, k, AES_BLOCK_SIZE, &aes, iv, AES_DECRYPT);//进行data key解密 /* void AES_cbc_encrypt (const unsigned char *in, unsigned char *out, const unsigned long length, const AES_KEY *key, //it will be changed during the encrypt&decrypt process, so it required to be reset each time unsigned char *ivec, // salt, it will be changed during the encrypt&decrypt process, so it required to be reset each time const int enc //AES_ENCRYPT for encrypt and other values for descrypt);*/ memcpy(kki, k, AES_BLOCK_SIZE);//获取data 加密密钥data key /*释放链表*/
Keychain *kc = keychain; free(keychain->key); keychain = keychain->next; delete kc; } fclose(fp); decrypt(filename, kki);//获取完整的data key,进而对filename进行相关的解密 return true; } }
不然的话就将生成的部分keychain释放。}
3.RENEW(fileName,policyName)
{
这个函数主要是使用新的策略取代旧的策略,所以我们需要做的就是更改策略所使用的区域
1.遍历文件:getFile(filename, ".metadata")
2.打开文件属性,并获取文件policyName:FILE *fp = fopen((path + '/' + filename + ".metadata").c_str(), "r");和fgets(oldPolicyName, POLICY_NAME_LENGTH, fp);
3.将S key进行恢复,然后使用新的策略进行加密
fread(key, sizeof(unsigned char), rsaSize * ssss_number, fp);//S key分成ssss_number几个部分。 for (int share = 0; share < ssss_number; ++share) { PolicyForClient oldPolicy(oldPolicyName, share); oldPolicy.decrypt(key + rsaSize * share)//将S解密出来PolicyForClient newPolicy(newPolicyName, share);!newPolicy.encrypt(key + rsaSize * share)//对S进行加密}这样的话就是用了新的策略将S key加密起来了
4.读出[data key]s:fread(k, sizeof(unsigned char), AES_BLOCK_SIZE, fp);
5.重新生成一个文件.metadata
三步走:使用newpolicyName取代oldpolicyName、使用新policy的加密后的S取代旧的,然后就是不变的[data key]S,全程S、data key没有改变,改变的只是policyName以及经过policyName处理的[S]policy
fp = fopen((path + '/' + filename + ".metadata").c_str(), "w"); fprintf(fp, "%s\n", newPolicyName);//nowPolicyName replace oldPolicyName fwrite(key, sizeof(unsigned char), rsaSize * ssss_number, fp);//S key fwrite(k, sizeof(unsigned char), AES_BLOCK_SIZE, fp);//[data key]s}
4. REVOKE:吊销policyName,这个需要跟key manager进行交互,相对来说麻烦一点。跟key manager通过使用socket进行信息之间的传递
bool Client::revoke(const char *policyName)
{ for (int share = 0; share < ssss_number; ++share) { char kmid[11]; snprintf(kmid, sizeof(kmid), "%d", share); remove((path + '/' + policyName + '.' + kmid + ".pubkey").c_str());//移走公钥? } PolicyForClient policy(policyName); return policy.revoke();}接着进入policy.revoke函数进行相关policy删除:bool PolicyForClient::revoke() const{
1.首先获取policyName,并用它来作为下一步的请求字符串request
2.重点来了const int challengeLength = Client::instance()->interact(request, requestLength, response, RESPONSE_BUFFER_LENGTH, keyManagerID) - 1;在这里我们可以看到调用了Client::instance()->interact,跟前面我们分析的那个key manager中的interact是不是有点眼熟。眼熟那就对了,Client跟Key manager是通过interact中的socket进行联系的,也就是客户端与服务器之间的所有的操作都是通过interact来进行的,具体的就是通过socket来进行的。
Client::instance()->interact{
步骤跟前面key manager中的interact相类似,唯一的区别就是我们将数据发送过去,等待key manager进行处理,然后将处理结果发给Client::instance()->interact
1. socket地址初始化
2. 建立socket以及跟key manager中的socket进行连接,为一下步的信息传递做铺垫
3. 发送信息,这里面一般要发送两个信息,if (sendn(sockfd, &len, sizeof(len)) < 0 || sendn(sockfd, request, requestLength) < 0),一个是传输的数据长度,一个是实际信息量
4. 然后等待key manager返回数据,在这里我们可以看到的就是responese以及len长度,这里是key manager处理的结果}
在interact之后,我们获取了返回信息responese以及长度len,接下来要做的就是根据responese来进行处理。
1). 如果*responese== EPHEMERIZER_RESPONSE_FAILED这说明操作失败,直接返回
2). 如果*responese== EPHEMERIZER_RESPONSE_OK_CPABE,那么我们需要分开讨论{
1. 在/temp_cpabe.cpabe中: FILE *fp = fopen((Client::instance()->getPath() + "/temp_cpabe.cpabe").c_str(), "w"); if (system(("cpabe-dec " + Client::instance()->getCpabePublicKeyFile() + ' ' + Client::instance()->getCpabePrivateKeyFile() + ' ' + Client::instance()->getPath() + "/temp_cpabe.cpabe").c_str()))
2. 在/temp_cpabe中:fp = fopen((Client::instance()->getPath() + "/temp_cpabe").c_str(), "r");然后进行获取前面提到的len长度的信息,requestLength = fread(request + 4, 1, challengeLength, fp) + 4;然后再和key manager进行交互Client::instance()->interact(request, requestLength, response, RESPONSE_BUFFER_LENGTH, keyManagerID);//client request}
}
}