4个版本 (2个重大变更)
0.4.1 | 2020年7月7日 |
---|---|
0.4.0 | 2020年5月18日 |
0.3.0 | 2020年4月29日 |
0.2.0 | 2020年4月20日 |
#2451 in 密码学
808 每月下载量
用于 10 个crate (3直接)
150KB
3.5K SLoC
Boneh, Boyen和Shachum的短群签名以及后来在ASM中作为BBS+进行改进,并在CDL的第4.3节中再次提及。
此crate实现了BBS+签名方案,允许对多个已提交的消息进行签名。
BBS+签名可以按照典型的密码学方式创建,其中签名者和签名持有者是同一方或两个不同的方。BBS+签名还可以用于生成签名知识证明和选择性披露零知识证明。要开始,您只需将此添加到您的Cargo.toml
。
[dependencies]
bbs = "0.4"
在主代码部分添加,以获取所需的全部特性、结构和函数。
use bbs::prelude::*;
密钥生成
BBS+支持两种类型的公钥。一种是在论文中描述的,其中消息特定生成器随机生成,另一种是类似BLS公钥的确定性版本,其消息特定生成器使用IETF的Hash to Curve算法计算,该算法与已知输入相结合,且是常数时间的。
generate(message_count: usize)
- 返回用于创建BBS+签名的密钥对
PublicKey
- w ← 𝔾2,h0,(h1,... ,hL) ← 𝔾1L
DeterministicPublicKey
- w ← 𝔾2. 这可以通过调用to_public_key
方法转换为公钥。
还有一个方便的类Issuer
也可以用于此。
let (pk, sk) = Issuer::new_keys(5).unwrap();
或
let (dpk, sk) = Issuer::new_short_keys(None);
let pk = dpk.to_public_key(5).unwrap();
签名
签名可以在签名者知道所有消息的地方进行,或者签名接收者事先承诺一些消息,然后签名者用剩余的消息完成签名。
创建签名
let (pk, sk) = Issuer::new_keys(5).unwrap();
let messages = vec![
SignatureMessage::hash(b"message 1"),
SignatureMessage::hash(b"message 2"),
SignatureMessage::hash(b"message 3"),
SignatureMessage::hash(b"message 4"),
SignatureMessage::hash(b"message 5"),
];
let signature = Signature::new(messages.as_slice(), &sk, &pk).unwrap();
assert!(signature.verify(messages.as_slice(), &pk).unwrap());
或
// Generated by the issuer
let (pk, sk) = Issuer::new_keys(5).unwrap();
// Done by the signature recipient
let message = SignatureMessage::hash(b"message_0");
let signature_blinding = Signature::generate_blinding();
let commitment = &pk.h[0] * &message + &pk.h0 * &signature_blinding;
// Completed by the signer
// `commitment` is received from the recipient
let messages = sm_map![
1 => b"message_1",
2 => b"message_2",
3 => b"message_3",
4 => b"message_4"
];
let blind_signature = BlindSignature::new(&commitment, &messages, &sk, &pk).unwrap();
// Completed by the recipient
// receives `blind_signature` from signer
// Recipient knows all `messages` that are signed
let signature = blind_signature.to_unblinded(&signature_blinding);
let mut msgs = messages
.iter()
.map(|(_, m)| m.clone())
.collect::<Vec<SignatureMessage>>();
msgs.insert(0, message.clone());
let res = signature.verify(msgs.as_slice(), &pk);
assert!(res.is_ok());
assert!(res.unwrap());
仅凭这一点,如果没有签名者完成由接收者生成并发送的承诺消息的知识证明,那么它本身是不安全的。重要的是,签名发行者完成这一步。为了简化,可以使用以下方式使用 Issuer
和 Prover
结构体来处理这一点。
let (pk, sk) = Issuer::new_keys(5).unwrap();
let signing_nonce = Issuer::generate_signing_nonce();
// Send `signing_nonce` to holder
// Recipient wants to hide a message in each signature to be able to link
// them together
let link_secret = Prover::new_link_secret();
let mut messages = BTreeMap::new();
messages.insert(0, link_secret.clone());
let (ctx, signature_blinding) =
Prover::new_blind_signature_context(&pk, &messages, &signing_nonce).unwrap();
// Send `ctx` to signer
let messages = sm_map![
1 => b"message_1",
2 => b"message_2",
3 => b"message_3",
4 => b"message_4"
];
// Will fail if `ctx` is invalid
let blind_signature = Issuer::blind_sign(&ctx, &messages, &sk, &pk, &signing_nonce).unwrap();
// Send `blind_signature` to recipient
// Recipient knows all `messages` that are signed
let mut msgs = messages
.iter()
.map(|(_, m)| m.clone())
.collect::<Vec<SignatureMessage>>();
msgs.insert(0, link_secret.clone());
let res =
Prover::complete_signature(&pk, msgs.as_slice(), &blind_signature, &signature_blinding);
assert!(res.is_ok());
证明
验证者要求证明者揭示一些已签名的消息(从零到所有消息),而剩余的消息则被隐藏。如果证明者同意,她将完成签名知识的证明和承诺值的证明。这些消息可以与其他零知识证明(如zkSNARKs或Bulletproofs)结合,例如边界检查或集合成员资格。如果是这种情况,隐藏的消息需要通过一个共同的盲化因子与其他证明链接。这个库为证明提供了三种消息分类,以适应这种灵活性。
- ProofMessage::Revealed:消息将变为验证者所知道。注意:密码学是在整数上操作的,而不是直接在字符串上操作。通常,会签名字符串的哈希。验证者将学习揭示的哈希,而不是消息内容。证明者必须发送预像,以便验证者可以在验证签名后检查哈希是否相等。
- ProofMessage::Hidden:消息不会显示给验证者。隐藏消息有两种类型。
- HiddenMessage::ProofSpecificBlinding:消息被隐藏,且不用于任何其他证明,盲化是仅针对此签名。
- HiddenMessage::ExternalBlinding:消息被隐藏,但也被用于另一个证明。例如,为了显示两个消息在两个签名或“链接”中是相同的,两个证明必须使用相同的盲化因子。这种类型将盲化因子和消息分组。
要开始零知识证明交换,验证者指示要揭示哪些消息,并提供一个nonce限制证明者的欺骗能力,即在没有知道实际消息或签名的情况下创建有效的证明。
验证者必须信任凭证的签名者,并知道消息结构,即索引1、2、3等处的消息是什么。
let (pk, sk) = Issuer::new_keys(5).unwrap();
let messages = vec![
SignatureMessage::hash(b"message_1"),
SignatureMessage::hash(b"message_2"),
SignatureMessage::hash(b"message_3"),
SignatureMessage::hash(b"message_4"),
SignatureMessage::hash(b"message_5"),
];
let signature = Signature::new(messages.as_slice(), &sk, &pk).unwrap();
let nonce = Verifier::generate_proof_nonce();
let proof_request = Verifier::new_proof_request(&[1, 3], &pk).unwrap();
// Sends `proof_request` and `nonce` to the prover
let proof_messages = vec![
pm_hidden!(b"message_1"),
pm_revealed!(b"message_2"),
pm_hidden!(b"message_3"),
pm_revealed!(b"message_4"),
pm_hidden!(b"message_5"),
];
let pok = Prover::commit_signature_pok(&proof_request, proof_messages.as_slice(), &signature)
.unwrap();
// complete other zkps as desired and compute `challenge_hash`
// add bytes from other proofs
let mut challenge_bytes = Vec::new();
challenge_bytes.extend_from_slice(pok.to_bytes().as_slice());
challenge_bytes.extend_from_slice(nonce.to_bytes().as_slice());
let challenge = ProofNonce::hash(&challenge_bytes);
let proof = Prover::generate_signature_pok(pok, &challenge).unwrap();
// Send `proof` and `challenge` to Verifier
match Verifier::verify_signature_pok(&proof_request, &proof, &nonce) {
Ok(_) => assert!(true), // check revealed messages
Err(_) => assert!(false), // Why did the proof failed
};
依赖项
~5MB
~100K SLoC