#签名 #bbs #消息 #aries #证明 #生成 #签名者

aries-bbssignatures

Hyperledger Aries的BBS+签名支持

1 个不稳定版本

0.1.0 2024年4月12日

#604 in 加密学

Apache-2.0

150KB
3K SLoC

Boneh, Boyen和Shachum的短群签名及后续在ASM中作为BBS+改进,并在CDL的第4.3节中再次提及。


此crate实现了BBS+签名方案,允许对多个已提交的消息进行签名。

BBS+签名可以以典型的加密方式创建,其中签名人或签名持有者是同一方,或者它们是两个不同的实体。BBS+签名也可以用于生成签名知识证明和选择性披露零知识证明。要开始,只需要将其添加到您的Cargo.toml中。

[dependencies]
aries_bbssignatures = "0.1"

在代码的主部分中添加以获取所需的全部特性和结构。

use aries_bbssignatures::prelude::*;

密钥生成

BBS+支持两种类型的公钥。一种是在论文中描述的,其中消息特定生成器是随机生成的,另一种是类似于BLS公钥的确定版本,其消息特定生成器使用IETF的Hash to Curve算法计算,该算法与已知输入相结合,具有常数时间复杂度。

generate(message_count: usize) - 返回用于创建BBS+签名的密钥对

PublicKey - w ← 𝔾₂, h₀, (h₁, ... , h_L) ← 𝔾₁^L

DeterministicPublicKey - w ← 𝔾₂. 这可以通过调用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,例如界限检查或集合成员资格。如果是这种情况,隐藏的消息需要使用一个公共的遮蔽因子与其它证明相关联。此crate提供了三种消息分类,以便适应这种灵活性。

  • ProofMessage::Revealed:消息将变得对验证者可见。注意:密码学在整数上操作,而不是直接在字符串上操作。通常,字符串的哈希值会被签名。验证者将学习到揭示的哈希值,而不是消息内容。证明者必须发送预映像,以便验证者可以在验证签名后检查哈希值是否相等。
  • ProofMessage::Hidden:消息不会显示给验证者。存在两种类型的隐藏消息。
    • HiddenMessage::ProofSpecificBlinding:消息被隐藏,且不用于任何其他证明,遮蔽是仅针对此签名。
    • HiddenMessage::ExternalBlinding:消息被隐藏,但也用于另一个证明。例如,为了展示两个消息在两个签名或“链接”中相同,必须为两个证明使用相同的遮蔽因子。这个类别将遮蔽因子和消息分组。

为了开始零知识证明交换,验证者指出哪些消息将被揭示,并提供了限制证明者作弊能力的随机数限制,即在没有了解实际消息或签名的情况下创建有效证明的能力。

验证者必须信任凭证的签名者并了解消息结构,即消息索引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
};

依赖项

~5.5MB
~107K SLoC