- SK
- The secret key for the signature scheme.
- PK
- The public key for the signature scheme.
- L
- The total number of signed messages.
- R
- The number of message indexes that are disclosed (revealed) in a proof-of-knowledge of a signature.
- U
- The number of message indexes that are undisclosed in a proof-of-knowledge of a signature.
- scalar
- An integer between 0 and r-1, where r is the prime order of the selected groups, defined by each ciphersuite (see also
Notation ). - input_message
- An input message to be signed by the signature scheme. An input_message can either be either an octet string or a scalar.
- generator
- A valid point on the selected subgroup of the curve being used that is employed to commit a value.
- signature
- The digital signature output.
- nonce
- A cryptographic nonce
- presentation_header (ph)
- A payload generated and bound to the context of a specific spk.
- dst
- The domain separation tag.
- I2OSP
- An operation that transforms a non-negative integer into an octet string, defined in Section 4 of
. Note, the output of this operation is in big-endian order. - OS2IP
- An operation that transforms a octet string into an non-negative integer, defined in Section 4 of
. Note, the input of this operation must be in big-endian order. - INVALID, ABORT
- Error indicators. INVALID refers to an error encountered during the Deserialization or Procedure steps of an operation. An INVALID value can be returned by a subroutine and handled by the calling operation. ABORT indicates that one or more of the initial constraints defined by the operation are not met. In that case, the operation will stop execution. An operation calling a subroutine that aborted must also immediately abort.

- a || b
- Denotes the concatenation of octet strings a and b.
- I \ J
- For sets I and J, denotes the difference of the two sets i.e., all the elements of I that do not appear in J, in the same order as they were in I.
- X[a..b]
- Denotes a slice of the array
`X`containing all elements from and including the value at index`a`until and including the value at index`b`. Note when this syntax is applied to an octet string, each element in the array`X`is assumed to be a single byte. - range(a, b)
- For integers a and b, with a <= b, denotes the ascending ordered list of all integers between a and b inclusive (i.e., the integers "i" such that a <= i <= b).
- length(input)
- Takes as input either an array or an octet string. If the input is an array, returns the number of elements of the array. If the input is an octet string, returns the number of bytes of the inputted octet string.

- E1, E2
- elliptic curve groups defined over finite fields. This document assumes that E1 has a more compact representation than E2, i.e., because E1 is defined over a smaller field than E2. For a pairing-friendly curve, this document denotes operations in E1 and E2 in additive notation, i.e., P + Q denotes point addition and x * P denotes scalar multiplication.
- G1, G2
- subgroups of E1 and E2 (respectively) having prime order r.
- GT
- a subgroup, of prime order r, of the multiplicative group of a field extension.
- e
- G1 x G2 -> GT: a non-degenerate bilinear map.
- r
- The prime order of the G1 and G2 subgroups.
- BP1, BP2
- base (constant) points on the G1 and G2 subgroups respectively.
- Identity_G1, Identity_G2, Identity_GT
- The identity element for the G1, G2, and GT subgroups respectively.
- hash_to_curve_g1(ostr, dst) -> P
- A cryptographic hash function that takes an arbitrary octet string as input and returns a point in G1, using the hash_to_curve operation defined in
and the inputted dst as the domain separation tag for that operation (more specifically, the inputted dst will become the DST parameter for the hash_to_field operation, called by hash_to_curve). - point_to_octets_g1(P) -> ostr, point_to_octets_g2(P) -> ostr
- returns the canonical representation of the point P for the respective subgroup as an octet string. This operation is also known as serialization.
- octets_to_point_g1(ostr) -> P, octets_to_point_g2(ostr) -> P
- returns the point P for the respective subgroup corresponding to the canonical representation ostr, or INVALID if ostr is not a valid output of the respective point_to_octets_g* function. This operation is also known as deserialization.
- subgroup_check(P) -> VALID or INVALID
- returns VALID when the point P is an element of the subgroup of order r, and INVALID otherwise. This function can always be implemented by checking that r * P is equal to the identity element. In some cases, faster checks may also exist, e.g.,
.

Scheme Definition defines the core operations and parameters for the BBS signature scheme.Utility Operations defines utilities used by the BBS signature scheme.Security Considerations describes a set of security considerations associated to the signature scheme.Ciphersuites defines the format of a ciphersuite, alongside a concrete ciphersuite based on the BLS12-381 curve.

A pairing-friendly elliptic curve, plus associated functionality given in Section 1.2 .A hash-to-curve suite as defined in , using the aforementioned pairing-friendly curve. This defines the hash_to_curve and expand_message operations, used by this document. get_random(n): returns a random octet string with a length of n bytes, sampled uniformly at random using a cryptographically secure pseudo-random number generator (CSPRNG) or a pseudo random function. See for recommendations and requirements on the generation of random numbers.

- Points in
`G*`will be serialized using the`point_to_octets_g*`implementation for a particular ciphersuite. - Non-negative integers will be serialized using
`I2OSP`with an output length of 8 bytes. - Scalars will be serialized using
`I2OSP`with a constant output length defined by a particular ciphersuite.

`hash_to_scalar`is defined in`messages_to_scalars`is defined in`calculate_domain`and`calculate_challenge`are defined inand correspondingly. `serialize`,`signature_to_octets`,`octets_to_signature`,`proof_to_octets`,`octets_to_proof`and`octets_to_pubkey`are defined in

- count (REQUIRED), a non-negative integer describing the number of generator points to create, which is determined in part by the number of signed messages.
- PK (OPTIONAL), a point of G2, representing the Signer's public key.

- The returned points should be indistinguishable from
`count`uniformly radom points of G1. This means that given only the points`H_1, ..., H_i`it should be infeasible to guess`H_(i+1)`(or any`H_j`with`j > i`), for any`i`between 1 and`count`. - The returned points must be unique with very high probability, that would not lessen the targeted security level of the ciphersuite. Specifically, for a security level
`k`, the probability of a collision should be at least`1/2^k`. - It should be infeasible to guess the discrete logarithm of the returned points, for any base, even with knowledge of the public parameters that were used to create those generators (like the
`generator_seed`value inHash to Generators ). Note that pseudo randomness does not necessarily imply this property. For example, an implementation that repeatably hashes a public seed value to create exponents`r_1, r_2, ..., r_count`(where`r_1 = hash(seed), r_2 = hash(r_1), ...`) and then returns the points`H_1 = P1 * r_1, H_2 = P_1 * r_2, ..., H_count = P_1 * r_count`would be insecure (given knowledge of the seed), but given knowledge of only the points`H_1, ..., H_count`, the sequence would appear random. - The returned points must be different from the Identity point of G1 as well as the constant point
`P1`defined by the ciphersuite. - Must be constant time for a specific
`count`value. - Must be deterministic.
- Must use proper domain separation for both the
`create_generators`procedure, as well as all of the internally-called procedures.

- message (REQUIRED), an input_message that can be either a scalar or an octet string (see
Terminology ). - index (OPTIONAL), a positive integer. The index the message has in the list of signed messages.

- It MUST return unique values for different
`msg`inputs. More specifically, the probability of a collision under reasonable cryptographic assumptions MUST be at most`1/2^k`, where`k`the security level of the targeted ciphersuite. - Different outputs MUST be independent. More specifically, knowledge of the
`scalar_1 = map_to_scalar(msg_1, idx_1)`, should not give any information on the value of`scalar_2 = map_to_scalar(msg_2, idx_2)`, for any other`(msg_2, idx_2)`input pair. - It MUST be deterministic.

- Two (2) valid points of the G1 subgroup, different from the identity point of G1 (i.e.,
`Abar, Bbar`, in ProofGen) - Three (3) integers representing scalars in the range of 1 to r-1 inclusive (i.e.,
`c, r2^, r3^`, in ProofGen). - A number of integers representing scalars in the range of 1 to r-1 inclusive, corresponding to the undisclosed from the proof messages (i.e.,
`m^_j1, ..., m^_jU`, in ProofGen, where U the number of undisclosed messages).

- Two (2) valid points of the G1 subgroup, each of which must not equal the identity point.
- Two (2) integers representing scalars in the range of 1 to r-1 inclusive.
- A set of integers representing scalars in the range of 1 to r-1 inclusive, corresponding to the undisclosed from the proof message commitments. This set can be empty (i.e., "()").
- One (1) integer representing a scalar in the range of 1 to r-1 inclusive, corresponding to the proof's challenge (
`c`).

For most pairing-friendly elliptic curves used in practice, the pairing operation e is undefined when its input points are not in the prime-order subgroups of E1 and E2. The resulting behavior is unpredictable, and may enable forgeries. Even if the pairing operation behaves properly on inputs that are outside the correct subgroups, skipping the subgroup check breaks the strong unforgeability property .

H2C_SUITE_ID is the suite ID of the hash-to-curve suite used to define the hash_to_curve function. CG_ID is the ID of the create generators used, i.e., `CREATE_GENERATORS_ID`as defined in thesection. MAP_TO_SCALAR_ID is the ID of the map_to_scalar operation, as defined in . ADD_INFO is an optional octet string indicating any additional information used to uniquely qualify the ciphersuite. When present this value MUST only contain ASCII encoded characters with codes between 0x21 and 0x7e (inclusive) and MUST end with an underscore (ASCII code: 0x5f), other than the last character the string MUST not contain any other underscores (ASCII code: 0x5f).

hash: a cryptographic hash function. octet_scalar_length: Number of bytes to represent a scalar value, in the multiplicative group of integers mod r, encoded as an octet string. It is RECOMMENDED this value be set to `ceil(log2(r)/8)`.octet_point_length: Number of bytes to represent a point encoded as an octet string outputted by the `point_to_octets_g*`function. It is RECOMMENDED that this value is set to`ceil(log2(p)/8)`.hash_to_curve_suite: The hash-to-curve ciphersuite id, in the form defined in . This defines the hash_to_curve_g1 (the hash_to_curve operation for the G1 subgroup, see the Notation section) and the expand_message (either expand_message_xmd or expand_message_xof) operations used in this document.expand_len: Must be defined to be at least `ceil((ceil(log2(r))+k)/8)`, where`log2(r)`and`k`are defined by each ciphersuite (see Section 5 infor a more detailed explanation of this definition). P1: A fixed point in the G1 subgroup, different from the point BP1 (i.e., the base point of G1, see ). This leaves the base point "free", to be used with other protocols, like key commitment and proof of possession schemes (for example, like the one described in Section 3.3 of ).

point_to_octets_g1: a function that returns the canonical representation of the point P for the G1 subgroup as an octet string. point_to_octets_g2: a function that returns the canonical representation of the point P for the G2 subgroup as an octet string. octets_to_point_g1: a function that returns the point P in the subgroup G1 corresponding to the canonical representation ostr, or INVALID if ostr is not a valid output of `point_to_octets_g1`.octets_to_point_g2: a function that returns the point P in the subgroup G2 corresponding to the canonical representation ostr, or INVALID if ostr is not a valid output of `point_to_octets_g2`.

- create_generators: the operation with which to create a set of generators. See
.

- map_to_scalars:
a function that maps a message to a scalars value, as defined in
.

ciphersuite_id: "BBS_BLS12381G1_XOF:SHAKE-256_SSWU_RO_H2G_HM2S_" hash: SHAKE-256 as defined in . octet_scalar_length: 32, based on the RECOMMENDED approach of `ceil(log2(r)/8)`.octet_point_length: 48, based on the RECOMMENDED approach of `ceil(log2(p)/8)`.hash_to_curve_suite: "BLS12381G1_XOF:SHAKE-256_SSWU_RO_" as defined in Appendix A.1 for the G1 subgroup.expand_len: 48 ( `= ceil((ceil(log2(r))+k)/8)`)P1: The G1 point returned from the `hash_to_generators`procedure (), with `count = 1`and generator_seed = ciphersuite_id || "BP_MESSAGE_GENERATOR_SEED". More specifically,P1 = "8929dfbc7e6642c4ed9cba0856e493f8b9d7d5fcb0c31ef8fdcd34d50648a56c795e106 e9eada6e0bda386b414150755"

point_to_octets_g1: follows the format documented in Appendix C section 1 of for the G1 subgroup, using compression (i.e., setting C_bit = 1). point_to_octets_g2: follows the format documented in Appendix C section 1 of for the G2 subgroup, using compression (i.e., setting C_bit = 1). octets_to_point_g1: follows the format documented in Appendix C section 2 of for the G1 subgroup. octets_to_point_g2: follows the format documented in Appendix C section 2 of for the G2 subgroup.

create_generators: the operation is using hash_to_generators as defined in , with generator_seed = ciphersuite_id || "MESSAGE_GENERATOR_SEED" and the expand_message and hash_to_curve_g1 defined by the hash_to_curve_suite, create_generators(count, PK) := hash_to_generators(count)

- map_to_scalar: map_to_scalar_as_hash (
)

Ciphersuite_ID: "BBS_BLS12381G1_XMD:SHA-256_SSWU_RO_H2G_HM2S_" hash: SHA-256 as defined in . octet_scalar_length: 32, based on the RECOMMENDED approach of `ceil(log2(r)/8)`.octet_point_length: 48, based on the RECOMMENDED approach of `ceil(log2(p)/8)`.hash_to_curve_suite: "BLS12381G1_XMD:SHA-256_SSWU_RO_" as defined in Section 8.8.1 of the for the G1 subgroup. expand_len: 48 ( `= ceil((ceil(log2(r))+k)/8)`)P1: The G1 point returned from the `hash_to_generators`procedure, with`count = 1`and generator_seed = ciphersuite_id || "BP_MESSAGE_GENERATOR_SEED". More specifically,P1 = "a8ce256102840821a3e94ea9025e4662b205762f9776b3a766c872b948f1fd225e7c596 98588e70d11406d161b4e28c9"

point_to_octets_g1: follows the format documented in Appendix C section 1 of for the G1 subgroup, using compression (i.e., setting C_bit = 1). point_to_octets_g2: follows the format documented in Appendix C section 1 of for the G2 subgroup, using compression (i.e., setting C_bit = 1). octets_to_point_g1: follows the format documented in Appendix C section 2 of for the G1 subgroup. octets_to_point_g2: follows the format documented in Appendix C section 2 of for the G2 subgroup.

create_generators: the operation is using hash_to_generators as defined in , with generator_seed = ciphersuite_id || "MESSAGE_GENERATOR_SEED" and the expand_message and hash_to_curve_g1 defined by the hash_to_curve_suite, create_generators(count, PK) := hash_to_generators(count)

- map_to_scalar: map_to_scalar_as_hash (
)

Prove possession of a valid signature. As defined above, a signature `(A, e)`, on messages`msg_1, ..., msg_L`is valid, if`A = B * 1/(e + SK)`, where`B`as in [1]. However we cannot reveal neither`A`,`e`nor`B`to the verifier (signature is uniquely identifiable and`B`will reveal information about the signed messages, even the undisclosed ones). To get around this, we need to hide the signature`(A, e)`and the value of`B`, in a way that will allow proving knowledge of such ellements with the aformentioned relationship (i.e., that`A = B * 1/(e + SK)`), without revealing their value. We do this by randomizing them. To do that, take uniformly random`r1`in`[1, r-1]`, and calculate,[2] Abar = A * r1, [3] Bbar = B * r1 + Abar * (-e) The values `(Abar, Bbar)`will be part of the proof and are used to prove possession of a BBS signature, without revealing the signature itself. Note that; if`Abar`and`Bbar`are constructed using a valid BBS signatures as above, then`Abar * SK = Bbar => e(Abar, PK) = e(Bbar, BP2)`where`SK`,`PK`the signer's secret and public key and`BP2`the base element in`G2`(used to create the signer__’__s`PK`, see). This last equation is something that the verifier can check. This also serves to bind the proof to the signer's `PK`.Prove that the disclosed messages are signed by that signature. Set the following, [4] D = P1 + Q_1 * domain + H_i1 * msg_i1 + ... + H_iR * msg_iR [5] r1' = r1 ^ -1 mod r If the `Abar`and`Bbar`values are constructed using a valid BBS signature as in [2] and [3], then the following equation will hold,[6] D = Bbar * r1' + Abar * (e * r1') - H_ji * msg_j1 - ... ... - H_jU * msg_jU

- Initial version

- Populated fixtures
- Added SHA-256 based ciphersuite
- Fixed typo in ProofVerify
- Clarify ASCII string usage in DST
- Added MapMessageToScalar test vectors
- Fix typo in ciphersuite name

- Variety of editiorial clarifications
- Clarified integer endianness
- Revised the encode for hash operation
- Shifted to using CSPRNG instead of PRF
- Removed total number of messages from proof verify operation
- Added deterministic proof fixtures
- Shifted to multiple CSPRNG calls to calculate random elements, instead of expand_message
- Updated hash_to_scalar to a single output

- Updated core operation based on new
academic paper - Variety of editorial updates
- Updated exception and error handling
- Added extension point for the operation with which the generators are created, allowing ciphersuites to define different operations for creating the generator points.
- Added extension point for the operation with which the input messages are mapped to scalar values, allowing ciphersuites to define different message-to-scalar mapping operations
- Added signature/proof fixtures with an empty header or an empty presentation header input
- Updated the fixtures to use variable length messages (one of which is now the empty message "")