目录
2.教程(Tutorial)
本章将介绍如何使用PBC库实现Boneh-Lynn-Shacham(BLS)签名方案。这是基于文件example/bls.c。
给出三个素数阶群G1、G2、GT(其实这三个都是循环群,素数阶群必定是循环群)。双线性映射从G1选择一个元素,从G2中选择一个元素,并输出一个GT中的元素。我们将这些与系统参数g一起发布,其中g是G2中随机选择的元素。
Alice想要签名一个信息,她生成公私钥对,私钥是Zr中的随机元素x,相应的公钥是g^x。
为了签名一个信息,Alice先将这个信息散列(hash)到G1的一些元素h上,然后输出签名h^x。
为了去验证签名σ,Bob检查e(h, g^x) = e(σ, g)。
我们现在使用PBC库将上述转化为C代码。
2.1.BLS签名
首先包含pbc/pbc.h:
#include <pbc.h>
然后初始化一个pairing:
pairing_t pairing;
char param[1024];
size_t count = fread(param, 1, 1024, stdin);
if(!count)
pbc_die("input error");
pairing_init_set_buf(pairing, param, count);
然后,我们在标准输入上为程序提供pairing参数。param子目录下的任何文件都可以,例如:
$ bls < param/a.param
我们需要几个element_t参数来保存系统参数、密钥和其它的数量。我么声明并初始化它们,
element_t g, h;
element_t public_key, secret_key;
element_t sig;
element_t temp1, temp2;
element_init_G2(g, pairing);
element_init_G2(public_key, pairing);
element_init_G1(h, pairing);
element_init_G1(sig, pairing);
element_init_GT(temp1, pairing);
element_init_GT(temp2, pairing);
element_init_Zr(secret_key, pairing);
生成系统参数,
element_random(g);
生成私钥,
element_random(secret_key);
生成相应的公钥,
element_pow_zn(public_key, g, secret_key);
当有信息需要签名时,我们首先使用一些标准hash函数计算它的hash值。许多库都可以实现这个操作,而且这个操作不涉及pairing,所以PBC库就没有提供这一步的函数。例如,我们的信息已经被hash,可能用到了其它的库。
假设这个消息的hash是"ABCDEF"(一个48比特的hash)。我们将这些字节映射到一个G1的元素h,
element_from_hash(h, "ABCDEF", 6);
然后签名它:
element_pow_zn(sig, h, secret_key);
为了去验证这个签名,我们比较应用于签名和系统参数的pairing输出,和应用于消息hash和公钥的pairing输出。如果两个pairing的输出是匹配的则签名是有效的。
pairing_apply(temp1, sig, g, pairing);
pairing_apply(temp2, h, public_key, pairing);
if(!element_cmp(temp1, temp2)){
printf("signature verifies\n");
}else{
printf("signature does not verify\n");
}
2.2.导入与导出
为了使签名有用,在某些阶段签名必须转换为字节进行存储或者传输:
int n = pairing_length_in_bytes_compressed_G1(pairing);
// 与下一行二选一
// int n = element_length_in_bytes_compressed(sig);
unsigned char *data = malloc(n);
element_to_bytes_compressed(data, sig);
在另一端,签名必须被解压:
element_from_bytes_compressed();
在上面的代码中省略_compression也可以,但是缓冲区data大约需要是两倍。
我们也可以只使用签名的x坐标,可以节省更多的空间:
int n = pairing_length_in_bytes_x_only_G1(pairing);
// 与下一行二选一
// int n = element_length_in_bytes_x_only(sig);
unsigned char *data = malloc(n);
element_to_bytes_compressed(data, sig);
但是,由于两个不同的点具有相同的x坐标,因此在验证过程中比较复杂。解决这个问题的一种方法是猜一个点,然后试着去验证。如果失败了我们再去尝试另一个。我们可以看出,这两个点的pairing输出是互逆的,避免了二次计算pairing的需要。(事实上,还有更好的办法解决这个问题。)
int n = pairing_length_in_bytes_x_only_G1(pairing);
// int n = element_length_in_bytes_x_only(sig);
unsigned char *data = malloc(n);
element_to_bytes_x_only(data, sig);
element_from_bytes_x_only(sig, data);
pairing_apply(temp1, sig, g, pairing);
pairing_apply(temp2, h, public_key, pairing);
if(!element_cmp(temp1, temp2)){
printf("signature verifies on first guess\n");
}else{
element_invert(temp1, temp1);
if(!element_cmp(temp1, temp2)){
printf("signature verifies on second guess\n");
}else{
printf("signature does not verify\n");
}
}
注:删除线部分是翻译不通顺或者可能有错误的地方。