9 releases (5 breaking)

0.6.0 May 30, 2021
0.5.0 May 17, 2021
0.4.0 May 10, 2021
0.3.2 Apr 30, 2021
0.1.1 Apr 6, 2021

#283 in Cryptography

Download history 48/week @ 2021-04-05 42/week @ 2021-04-12 36/week @ 2021-04-19 55/week @ 2021-04-26 35/week @ 2021-05-03 32/week @ 2021-05-10 32/week @ 2021-05-17 15/week @ 2021-05-24 25/week @ 2021-05-31

98 downloads per month
Used in ockam

Apache-2.0

120KB
2.5K SLoC

signature_bbs_plus

crate docs license discuss

Ockam is a library for building devices that communicate securely, privately and trustfully with cloud services and other devices.

In order to support a variety of proving protocols, this crate implements the BBS+ signature scheme which can be used to generate zero-knowledge proofs about signed attributes and the signatures themselves.

The main Ockam has optional dependency on this crate.

Usage

Add this to your Cargo.toml:

[dependencies]
signature_bbs_plus = "0.6.0"

Crate Features

[dependencies]
signature_bbs_plus = { version = "0.6.0"   , default-features = false }

Please note that Cargo features are unioned across the entire dependency graph of a project. If any other crate you depend on has not opted out of signature_bbs_plus default features, Cargo will build signature_bbs_plus with the std feature enabled whether or not your direct dependency on signature_bbs_plus has default-features = false.

API

Generating Keys

The BBS+ scheme allows the Signer and Holder to be two separate parties. This is often the case, particularly in the case of verifiable credentials.

To generate a new key pair for signing, call the Issuer::new_keys API. A Short Group Signature allows a set of messages to be signed with a single key. BBS+ can sign any number of messages at the expense of a bigger public key. This implementation uses curve BLS12-381 and Blake2b-512 as a hash.

let (public_key, secret_key) = Issuer::new_keys(&mut rand::thread_rng())?;

Message Generators

Message Generators are per-message cryptographic information input into the BBS+ algorithm. They are derived from the public key, and the number of messages the key will be used to sign.

Signing

To sign messages, call the Issuer::sign API.

let (public_key, secret_key) = Issuer::new_keys(&mut rand::thread_rng())?;
let num_messages = 4;
let generators = MessageGenerators::from_public_key(public_key, num_messages);
let messages = [
    Message::hash(b"message 1"),
    Message::hash(b"message 2"),
    Message::hash(b"message 3"),
    Message::hash(b"message 4"),
];

let signature = Issuer::sign(&secret_key, &generators, &messages)?;

Blinding Signatures

To create blind signatures, we first need to establish a blind signature context. This is done with the Prover::new_blind_signature_context API. This function takes an optional slice of pre-committed messages. In this example, an empty slice is used, indicating no pre-committed messages. The generators, a random nonce, and the RNG are also used.

With the context and secret key, the blind signature is created by calling Issuer::blind_sign.

let nonce = Nonce::random(&mut rng);

let (context, blinding) =
    Prover::new_blind_signature_context(&mut [][..], &generators, nonce, &mut rng)?;
let mut messages = [
    (0, Message::hash(b"firstname")),
    (1, Message::hash(b"lastname")),
    (2, Message::hash(b"age")),
    (3, Message::hash(b"allowed")),
];

let blind_signature =
    Issuer::blind_sign(&context, &secret_key, &generators, &mut messages[..], nonce)?;

Unblinding Signatures

Unblinding the signature uses the blinding information provided by the blinding signature context. The function to_unblinded takes the blinding and returns a Signature.

let signature = blind_signature.to_unblinded(blinding);

Verification

Once the signature has been unblinded, it can be used to verify the messages, using the public key. This is done by calling the Signature::verify function. Calling Choice::unwrap_u8 on the result of verify returns 1 when verification succeeds.

let signature = blind_signature.to_unblinded(blinding);

let messages = [
    Message::hash(b"message 1"),
    Message::hash(b"message 2"),
    Message::hash(b"message 3"),
    Message::hash(b"message 4"),
];
let res = signature.verify(&public_key, &generators, messages.as_ref());
assert_eq!(res.unwrap_u8(), 1);

Full Example - Blinding, Unblinding, Verifying

use short_group_signatures_core::{error::Error, lib::*};
use signature_bbs_plus::{Issuer, MessageGenerators, Prover};

fn main() -> Result<(), Error> {
    let mut rng = rand::thread_rng();
    let (public_key, secret_key) = Issuer::new_keys(&mut rng)?;
    let num_messages = 4;
    let generators = MessageGenerators::from_public_key(public_key, num_messages);
    let nonce = Nonce::random(&mut rng);

    let (context, blinding) =
        Prover::new_blind_signature_context(&mut [][..], &generators, nonce, &mut rng)?;
    let mut messages = [
        (0, Message::hash(b"firstname")),
        (1, Message::hash(b"lastname")),
        (2, Message::hash(b"age")),
        (3, Message::hash(b"allowed")),
    ];

    let blind_signature =
        Issuer::blind_sign(&context, &secret_key, &generators, &mut messages[..], nonce)?;

    let signature = blind_signature.to_unblinded(blinding);

    // Remove index
    let messages = [messages[0].1, messages[1].1, messages[2].1, messages[3].1];

    let res = signature.verify(&public_key, &generators, messages.as_ref());
    assert_eq!(res.unwrap_u8(), 1);
    Ok(())
}

License

This code is licensed under the terms of the Apache License 2.0.

Dependencies

~4.5MB
~86K SLoC