目錄
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");
}
}
注:刪除線部分是翻譯不通順或者可能有錯誤的地方。