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 密码学

Download history 177/week @ 2024-03-13 220/week @ 2024-03-20 147/week @ 2024-03-27 189/week @ 2024-04-03 333/week @ 2024-04-10 264/week @ 2024-04-17 120/week @ 2024-04-24 177/week @ 2024-05-01 164/week @ 2024-05-08 136/week @ 2024-05-15 178/week @ 2024-05-22 159/week @ 2024-05-29 179/week @ 2024-06-05 225/week @ 2024-06-12 232/week @ 2024-06-19 132/week @ 2024-06-26

808 每月下载量
用于 10 个crate (3直接)

Apache-2.0

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());

仅凭这一点,如果没有签名者完成由接收者生成并发送的承诺消息的知识证明,那么它本身是不安全的。重要的是,签名发行者完成这一步。为了简化,可以使用以下方式使用 IssuerProver 结构体来处理这一点。

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