Module BBS
BBS signature scheme
The BBS signature scheme (also known as the Boneh-Boyen-Shacham signature scheme) is a cryptographic signature scheme based on pairing-based cryptography: the BBS scheme is particularly notable for its use of bilinear pairings on elliptic curves, which enable efficient verification and compact signatures.
It is a probabilistic digital signature scheme based on the discrete logarithm problem, i.e. the difficult of finding the dicrete logarithm in a gruop of prime order.
The BBS signature scheme utilizes two elliptic curves in its construction: E1: y ^ 2 = x ^ 3 + 4 defined over the finite field GF(p).
E2: y ^ 2 = x ^ 3 + 4 * (I + 1) where I ^ 2 + 1 = 0 defined over the finite field GF(p^2)
where p = (t - 1)^2 * (t^4 - t^2 + 1) / 3 + t and t = -2^63 - 2^62 - 2^60 - 2^57 - 2^48 - 2^16.
Let be observed that p is not a prime number and the two subgroups G1 and G2 of E1 and E2 are the two groups used for the pairing having the same order r = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 which is a prime factor of p.
Public keys and key generation are considered as points in G2 and signatures as point in G1. This choice allows to provide short signatures with strong security guarantees.
Functions
ciphersuite (hash) | Return a specific ciphersuite based on the provided hash name. |
keygen (ciphersuite, key_material, key_info, key_dst) | Generate a secret key (sk) for use in cryptographic operations. |
hash_to_field_m1_c2 (ciphersuite, msg, dst) | Hash a message into two field elements in the prime field of an elliptic curve. |
hash_to_curve (ciphersuite, msg, dst) | Hash a message to a point on an elliptic curve. |
create_generators (ciphersuite, count) | Create a set of cryptographic generators. |
messages_to_scalars (ciphersuite, messages) | Convert a list of messages, a vector of octets, into a list of scalar values using a cryptographic hash function. |
sign (ciphersuite, sk, header, messages) | The Sign operation returns a BBS signature from a secret key (sk), over a header and a set of messages. |
octets_to_pub_key (pk) | Ensure that the public key is valid, in the right subgroup, and not the identity element. |
verify (ciphersuite, pk, signature, header, messages) | Validate a BBS signature. |
calculate_random_scalars (count) | Generate a table of random scalars that are uniformly distributed modulo the ECP order. |
proof_gen (ciphersuite, pk, signature, header, ph, messages, indexes) | Allow a user to prove knowledge of a valid signature while selectively revealing some messages and keeping others hidden. |
proof_verify (ciphersuite, pk, header, ph, messages, indexes) | It is responsible for validating a BBS proof. |
Functions
- ciphersuite (hash)
-
Return a specific ciphersuite based on the provided hash name.
There are two possibilities for the output: the configuration for SHAKE-256 or the configuration for SHA-256.Parameters:
- hash a string with the name of the hash function, sha256 or shake256
Returns:
-
a ciphersuite configuration
Usage:
bbs = require'crypto_bbs' **select the hash sha256 suite = bbs.ciphersuite('sha256') **print for example the ciphersuite ID print(suite.ciphersuite_ID) **print: BBS_BLS12381G1_XMD:SHA-256_SSWU_RO_
- keygen (ciphersuite, key_material, key_info, key_dst)
-
Generate a secret key (sk) for use in cryptographic operations.
The function uses a provided ciphersuite, key material, key info, and domain separation tag (DST) to derive the secret key. The ciphersuite is a table containing the cipher suite configuration. Key material is an optional input used as the seed for key generation. If not provided, a secure random value of 32 bytes is generated. Key info is an optional addition information. If not provided, it defaults to an empty octet. key DST is an optional domain separation tag. If not provided, it defaults to the ciphersuite's ciphersuite_ID concatenated with the string 'KEYGEN_DST_'.Parameters:
- ciphersuite
- key_material
- key_info
- key_dst
Returns:
-
sk, a private key 32 bytes long
Usage:
bbs = require'crypto_bbs' **generate ciphersuite and key_m, key_info, key_dst as random octet of 32 bytes ciphersuite = bbs.ciphersuite('sha256') key_material = O.random(32) key_info = O.random(32) key_dst = O.randopm(32) **calculate the private key sk = bbs.keygen(ciphersuite, key_material, key_info, key_dst)
- hash_to_field_m1_c2 (ciphersuite, msg, dst)
-
Hash a message into two field elements in the prime field of an elliptic curve.
This is a common operation in cryptographic protocols,
where messages need to be mapped to field elements. The third input of the function, the domain separation tag, ensures that the same input message can be hashed differently for different purposes, preventing collisions or unintended reuse of hash outputs.Parameters:
- ciphersuite
- msg the input message to be hashed
- dst a domain separation tag
Returns:
-
a table containing the two field elements
Usage:
bbs = require'crypto_bbs' **define a ciphersuite, a DST and a random message ciphersuite = bbs.ciphersuite('sha256') DST_hash_to_field = 'QUUX-V01-CS02-with-BLS12381G1_XMD:SHA-256_SSWU_RO_' msg = O.random(32) **return the table H. H[1] and H[2] will be the two field elements H = bbs.hash_to_field_m1_c2(ciphersuite, msg, DST_hash_to_field)
- hash_to_curve (ciphersuite, msg, dst)
-
Hash a message to a point on an elliptic curve. This is a common operation in cryptographic protocols, where messages
need to be mapped to curve points for operations like signing and verification.
It uses hash_to_field_m1_c2 function to hash the message into two field elements
that are mapped to a point on the elliptic curve.
Parameters:
- ciphersuite
- msg the input message to be hashed
- dst a domain separation tag
Returns:
-
the final curve point after clearing the cofactor
Usage:
bbs = require'crypto_bbs' **define a ciphersuite, a DST and a random message ciphersuite = bbs.ciphersuite('sha256') DST_hash_to_field = 'QUUX-V01-CS02-with-BLS12381G1_XMD:SHA-256_SSWU_RO_' msg = O.random(32) **return the curve point P = bbs.hash_to_curve(ciphersuite, msg, DST_hash_to_field)
- create_generators (ciphersuite, count)
-
Create a set of cryptographic generators.
These generators are points on an elliptic curve and are used in operations like signing and verification. The function ensures that the generators are created deterministically and securely, based on a seed value and domain separation tags.Parameters:
- ciphersuite
- count the number of generators to create
Returns:
-
the first 'count' generators from the ciphersuite.GENERATORS table
Usage:
bbs = require'crypto_bbs' **define a ciphersuite and a count ciphersuite = bbs.ciphersuite('sha256') count = 5 **return a table G of 5 generators G = bbs.create_generators(ciphersuite,count)
- messages_to_scalars (ciphersuite, messages)
-
Convert a list of messages, a vector of octets, into a list of scalar values using a cryptographic hash function.
Parameters:
- ciphersuite
- messages a set of messages
Returns:
-
a vector of scalars stored as octet
Usage:
bbs = require'crypto_bbs' **define a ciphersuite and a set of random messages ciphersuite = bbs.ciphersuite('sha256') map_messages_to_scalar_messages = { O.random(32), O.random(64), O.random(16) } **return a vector of octets output_scalar = bbs.messages_to_scalars(ciphersuite,map_messages_to_scalar_messages)
- sign (ciphersuite, sk, header, messages)
-
The Sign operation returns a BBS signature from a secret key (sk), over a header and a set of messages.
It uses thecore_sign
function, that computes a deterministic signature from a secret key (sk), a set of generators (points of G1) and optionally a header and a vector of messages.Parameters:
- ciphersuite
- sk the secret key as an octet
- header
- messages array of octet strings stored as octet
Returns:
-
the signature
Usage:
bbs = require'crypto_bbs' **define a ciphersuite, a secret key, a public key, an header and a message ciphersuite = bbs.ciphersuite('sha256') SECRET_KEY = "60e55110f76883a13d030b2f6bd11883422d5abde717569fc0731f51237169fc" HEADER = "11223344556677889900aabbccddeeff" SINGLE_MSG_ARRAY = { O.from_hex("9872ad089e452c7b6e283dfac2a80d58e8d0ff71cc4d5e310a1debdda4a45f02") } **calculate the signature for the given message output_signature = bbs.sign(ciphersuite, BIG.new(O.from_hex(SECRET_KEY)), O.from_hex(HEADER), SINGLE_MSG_ARRAY)
- octets_to_pub_key (pk)
-
Ensure that the public key is valid, in the right subgroup, and not the identity element.
It converts the public key (pk) into a point W on the elliptic curve in G2 and checks if W is in the correct subgroup.
Parameters:
- pk the public key
Returns:
-
the point W
Usage:
bbs = require'crypto_bbs' **define a ciphersuite and a public key ciphersuite = bbs.ciphersuite('sha256') PUBLIC_KEY = "a820f230f6ae38503b86c70dc50b61c58a77e45c39ab25c0652bbaa8fa136f2851bd4781c9dcde39fc9d1d52c9e60268061e7d7632171d 91aa8d460acee0e96f1e7c4cfb12d3ff9ab5d5dc91c277db75c845d649ef3c4f63aebc364cd55ded0c" **calculate the point W W = bbs.octets_to_pub_key(O.from_hex(PUBLIC_KEY))
- verify (ciphersuite, pk, signature, header, messages)
-
Validate a BBS signature. To do this it uses the
core_verify
function, which verify if the signature is valid.Parameters:
- ciphersuite
- pk the public key as an octet
- signature as an octet
- header
- messages an array of octet strings as octet
Returns:
-
a boolean, true if the signature is valid, false otherwise
Usage:
**from the usage in the sign function, check if the signature is valid if bbs.verify(ciphersuite, O.from_hex(PUBLIC_KEY), output_signature, O.from_hex(HEADER), SINGLE_MSG_ARRAY) then print ("valid signature") else print("invalid signature") end **print:valid signature
- calculate_random_scalars (count)
-
Generate a table of random scalars that are uniformly distributed modulo the ECP order.
Parameters:
- count number of random scalars to generate
Returns:
-
a table of uniformly distributed random scalars modulo the ECP order
Usage:
**generate a table of 5 random scalars T = bbs.calculate_random_scalars(5)
- proof_gen (ciphersuite, pk, signature, header, ph, messages, indexes)
-
Allow a user to prove knowledge of a valid signature while selectively revealing some messages and keeping others hidden.
It uses other functions during the process. In particular, the
proof_init()
function constructs commitments using the signature, public key, message generators, and random scalars. Theproof_challenge_calculate
function generates a challenge value from the commitments. Theproof_finalize
function computes the final proof values based on the challenge and initial commitments.Parameters:
- ciphersuite
- pk the public key
- signature
- header
- ph the presentation header
- messages an array of messages
- indexes an array of indexes of messages the prover wants to reveal
Returns:
-
a zero-knowledge proof that can be verified without revealing the entire signature.
Usage:
bbs = require'crypto_bbs' **define ciphersuite, sk, pk, header, single message array, presentation header and calculate a valid signature ciphersuite = bbs.ciphersuite('sha256') SECRET_KEY = "60e55110f76883a13d030b2f6bd11883422d5abde717569fc0731f51237169fc" PUBLIC_KEY = "a820f230f6ae38503b86c70dc50b61c58a77e45c39ab25c0652bbaa8fa136f2851bd4781c9dcde39fc9d1d52c9e60268061e7d7 632171d91aa8d460acee0e96f1e7c4cfb12d3ff9ab5d5dc91c277db75c845d649ef3c4f63aebc364cd55ded0c" HEADER = "11223344556677889900aabbccddeeff" SINGLE_MSG_ARRAY = { O.from_hex("9872ad089e452c7b6e283dfac2a80d58e8d0ff71cc4d5e310a1debdda4a45f02") } PRESENTATION_HEADER = O.from_hex("bed231d880675ed101ead304512e043ade9958dd0241ea70b4b3957fba941501") output_signature = bbs.sign(ciphersuite, BIG.new(O.from_hex(SECRET_KEY)), O.from_hex(PUBLIC_KEY), O.from_hex(HEADER), SINGLE_MSG_ARRAY) **return the proof like an octet pg_output = bbs.proof_gen(ciphersuite, O.from_hex(PUBLIC_KEY), output_signature, O.from_hex(HEADER), PRESENTATION_HEADER, SINGLE_MSG_ARRAY, {1})
- proof_verify (ciphersuite, pk, header, ph, messages, indexes)
-
It is responsible for validating a BBS proof. This proof was generated by proof_gen and allows a verifier to confirm that a signer possesses a valid BBS
signature while selectively revealing only some signed messages.
It uses some other functions: the
octets_to_proof
function converts the proof octet string into its mathematical components (group elements and scalars). Theproof_verify_init
function computes the expected commitments for disclosed and undisclosed messages. Theproof_challenge_calculate
ensures that the challenge scalar was computed correctly.Parameters:
- ciphersuite
- pk the public key
- header
- ph the presentation header
- messages an array of messages
- indexes an rray of indexes specifying which messages were disclosed
Returns:
-
true if the proof is valid, false otherwise
Usage:
**from the usage in the proof_gen function, check if the proof is valid if bbs.proof_verify(ciphersuite, O.from_hex(PUBLIC_KEY), pg_output, O.from_hex(HEADER), PRESENTATION_HEADER, SINGLE_MSG_ARRAY, {1}) then print("valid proof") else print("invalid proof") end **print: valid proof