BLS签名-使用PBC库
本篇博客将介绍如何使用PBC库实现Boneh-Lynn-Shacham (BLS)签名方案,该程序的源代码文件是
example/bls.c
我们有三个阶为素数r的群G1、G2、GT,和一个双线性映射,它能把一个来自G1的元素和一个来自G2的元素映射到GT对应的元素上。我们把这些与一个系统参数g一起发布,g是G2中随机选择的一个元素
Alice选择一个要签署的消息,她通过如下的方法生成公钥和私钥,她的私钥是1到r之间的随机数,她对应的公钥是 gx g x
为了签名一个消息,Alice将这条消息的hash值映射到G1的元素h上,然后输出这个签名 hx h x
为了验证签名σ,Bob验证这个等式 e(h,gx)=e(σ,g) e ( h , g x ) = e ( σ , g )
现在我们使用PBC库将这些转化为c代码
首先我们包含pbc/pbc.h文件
#include <pbc.h>
然后我们初始化一个双线性配对
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);
接着,我们通过改变我们程序的标准输入重定向来接收双线性配对的参数,
param
子目录下的任意一个文件都可以,比如说:bls < param/a.param
我们需要几个
element
变量去保存系统参数。我们声明并初始化他们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值。许多的库可以这样做,而且这个操作不涉及双线性配对,因此PBC不提供这一步的函数。公布这个消息的hash值是“ABCDEF”(一个48位的hash值),我们将这些比特映射到G1的一个元素h上
element_from_hash(h, "ABCDEF", 6);
生成签名
element_pow_zn(sig, h, secret_key);
为了验证这个签名,我们将签名、系统参数g和双线性配对生成的值与h、公钥和双线性配对生成的值进行比较。如果这两个输出匹配,说明这个签名是有效的
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"); }
为了有用,在某些阶段必须将签名转化为字节才能存储或传输
int n = pairing_length_in_bytes_compressed_G1(pairing); // Alternatively: // int n = element_length_in_bytes_compressed(sig); unsigned char *data = malloc(n); element_to_bytes_compressed(data, sig);
在另一方面,这个签名必须能够解压
element_from_bytes_compressed(sig, data);
省略 _compressed 在上面的代码中同样也能工作,但是缓冲区的大小大概会变为两倍
通过使用仅仅适用于签名的x-coordinate,我们能够节省更多的空间
int n = pairing_length_in_bytes_x_only_G1(pairing); // Alternative: // int n = element_length_in_bytes_x_only(sig); unsigned char *data = malloc(n); element_to_bytes_compressed(data, sig);
但是由于有两个不同的点有相同的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_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"); } }