Documentation ¶
Overview ¶
Package revocation implements the RSA-B accumulator and associated zero knowledge proofs, introduced in "Dynamic Accumulators and Application to Efficient Revocation of Anonymous Credentials", Jan Camenisch and Anna Lysyanskaya, CRYPTO 2002, DOI https://doi.org/10.1007/3-540-45708-9_5, http://static.cs.brown.edu/people/alysyans/papers/camlys02.pdf, and "Accumulators with Applications to Anonymity-Preserving Revocation", Foteini Baldimtsi et al, IEEE 2017, DOI https://doi.org/10.1109/EuroSP.2017.13, https://eprint.iacr.org/2017/043.pdf.
In short, revocation works as follows.
- Revokable credentials receive a "nonrevocation witness" consisting of two numbers, u and e, of which e is added to the credential as a new (hidden) "nonrevocation attribute".
- The issuer publishes an Accumulator, i.e a bigint Nu (the greek letter 𝛎). The witness is valid only if u^e = Nu mod N where N is the modulus of the (Idemix) public key of the issuer, i.e. e is "accumulated" in Nu.
- The client can during an IRMA disclosure session prove in zero knowledge that it knows numbers u and e such that (1) u^e = Nu mod N (2) e equals the credential's nonrevocation attribute, from which the verifier concludes that the credential is not currently revoked.
- The issuer can revoke a credential by removing its nonrevocation attribute e from the accumulator, by (1) Updating the accumulator value as follows: NewNu := Nu^(1/e mod (P-1)*(Q-1)) where P, Q is the issuer Idemix private key (2) Broadcasting (NewNu, e) to all IRMA apps and verifiers (3) All IRMA clients update their nonrevocation witness, using an algorithm taking the broadcast message and the client's current witness, resulting in a new u which is such that u^e = NewNu mod N. This algorithm is guaranteed to fail for the credential containing the revoked nonrevocation attribute e.
To keep track of previous and current accumulators, each Accumulator has an index which is incremented each time a credential is revoked and the accumulator changes value.
Issuers supporting revocation use ECDSA private/public keys to sign the accumulator update messages. All IRMA participants (client, verifier, issuer) require the latest revocation record to be able to function. The client additionally needs to know the complete chain of all events to be able to update its witness to the latest accumulator.
Notes ¶
By revoking, the issuer changes the value of the accumulator, of which all IRMA clients and verifiers need to be made aware before the client can prove to the verifier that its credential is not revoked against the new accumulator. If the client and verifier do not agree on the current value of the accumulator (for example, the client has not received all revocation broadcast messages while the verifier has), then the client cannot prove nonrevocation, leading the verifier to reject the client. The issuer thus has an important responsibility to ensure that all its revocation broadcast messages are always available to all IRMA participants.
If one thinks of the accumulator as a "nonrevocation public key", then the witness can be thought of as a "nonrevocation signature" which verifies against that public key (either directly or in zero knowledge). (That this "nonrevocation public key" changes when a credential is revoked, i.e. the accumulator is updated, has however no equivalent in signature schemes.)
Unlike ours, accumulators generally have both an Add and Remove algorithm, adding or removing stuff from the accumulator. The RSA-B has the property that the Add algorithm does nothing, i.e. all revocation witnesses e are added to it "automatically", and only removing one from the accumulator actually constitutes work (and broadcasting update messages).
In the literature the agent that is able to revoke (using a PrivateKey) is usually called the "revocation authority", which generally need not be the same agent as the issuer. In IRMA the design choice was made instead that the issuer is always the revocation authority.
Index ¶
- Constants
- Variables
- func NewProofRandomizer() *big.Int
- type Accumulator
- type Event
- type EventList
- type Hash
- type Proof
- type ProofCommit
- type SignedAccumulator
- type Update
- func (update *Update) MarshalCBOR() ([]byte, error)
- func (update *Update) MarshalJSON() ([]byte, error)
- func (update *Update) Prepend(eventlist *EventList) error
- func (update *Update) Product(from uint64) *big.Int
- func (update *Update) UnmarshalCBOR(data []byte) error
- func (update *Update) UnmarshalJSON(bts []byte) error
- func (update *Update) Verify(pk *gabikeys.PublicKey) (*Accumulator, error)
- type Witness
Constants ¶
const (
HashAlgorithm = multihash.SHA2_256
)
Variables ¶
var ( ErrorRevoked = errors.New("revoked") Parameters = struct { AttributeSize uint // maximum size in bits for prime e ChallengeLength uint // k' = len(SHA256) = 256 ZkStat uint // k” = 128 twoZk, bTwoZk, b *big.Int // 2^(k'+k”), B*2^(k'+k”+1), 2^AttributeSize }{ AttributeSize: 195, ChallengeLength: 256, ZkStat: 128, } )
var Logger *logrus.Logger
Functions ¶
func NewProofRandomizer ¶
NewProofRandomizer returns a bigint suitable for use as the randomizer in a nonrevocation zero knowledge proof.
Types ¶
type Accumulator ¶
Accumulator is an RSA-B accumulator against which clients with a corresponding Witness having the same Index can prove that their witness is accumulated, i.e. not revoked.
func (*Accumulator) Remove ¶
func (acc *Accumulator) Remove(sk *gabikeys.PrivateKey, e *big.Int, parent *Event) (*Accumulator, *Event, error)
Remove generates a new accumulator with the specified e removed from it.
func (*Accumulator) Sign ¶
func (acc *Accumulator) Sign(sk *gabikeys.PrivateKey) (*SignedAccumulator, error)
Sign the accumulator into a SignedAccumulator (c.f. SignedAccumulator.UnmarshalVerify()).
type Event ¶
type Event struct { Index uint64 `json:"i" gorm:"primary_key;column:eventindex"` E *big.Int `json:"e"` ParentHash Hash `json:"parenthash"` }
Event contains the data clients need to update to the Accumulator of the specified index, after it has been updated by the issuer by revoking. Forms a chain through the ParentHash which is the SHA256 hash of its parent.
type EventList ¶
type EventList struct { Events []*Event // ComputeProduct enables computation of the product of all revocation integers // present in Events during unmarshaling. ComputeProduct bool `json:"-"` // contains filtered or unexported fields }
func FlattenEventLists ¶
func NewEventList ¶
func (*EventList) MarshalCBOR ¶
func (*EventList) MarshalJSON ¶
func (*EventList) UnmarshalCBOR ¶
func (*EventList) UnmarshalJSON ¶
func (*EventList) Verify ¶
func (el *EventList) Verify(acc *Accumulator) error
type Hash ¶
type Hash multihash.Multihash
Hash represents a SHA256 hash and has marshaling methods to/from JSON.
func (Hash) MarshalJSON ¶
func (*Hash) UnmarshalJSON ¶
type Proof ¶
type Proof struct { Cr *big.Int `json:"C_r"` // Cr = g^r2 * h^r3 = g^epsilon * h^zeta Cu *big.Int `json:"C_u"` // Cu = u * h^r2 Nu *big.Int `json:"-"` // nu = Cu^e * h^(-e*r2) = Cu^alpha * h^-beta Challenge *big.Int `json:"-"` Responses map[string]*big.Int `json:"responses"` SignedAccumulator *SignedAccumulator `json:"sacc"` // contains filtered or unexported fields }
Proof is a proof that a Witness is valid against the Accumulator from the specified SignedAccumulator.
func (*Proof) ChallengeContributions ¶
func (*Proof) SetExpected ¶
SetExpected sets certain values of the proof to expected values, inferred from the containing proofs, before verification.
type ProofCommit ¶
type ProofCommit struct {
// contains filtered or unexported fields
}
ProofCommit contains the commitment state of a nonrevocation Proof.
func NewProofCommit ¶
func NewProofCommit(key *gabikeys.PublicKey, witn *Witness, randomizer *big.Int) ([]*big.Int, *ProofCommit, error)
NewProofCommit performs the first move in the Schnorr zero-knowledge protocol: committing to randomizers.
func (*ProofCommit) BuildProof ¶
func (c *ProofCommit) BuildProof(challenge *big.Int) *Proof
type SignedAccumulator ¶
type SignedAccumulator struct { Data signed.Message `json:"data"` PKCounter uint `json:"pk"` Accumulator *Accumulator `json:"-"` // Accumulator contained in this instance, set by UnmarshalVerify() }
SignedAccumulator is an Accumulator signed with the issuer's ECDSA key, along with the key index.
func (*SignedAccumulator) UnmarshalVerify ¶
func (s *SignedAccumulator) UnmarshalVerify(pk *gabikeys.PublicKey) (*Accumulator, error)
UnmarshalVerify verifies the signature and unmarshals the accumulator (c.f. Accumulator.Sign()).
type Update ¶
type Update struct { SignedAccumulator *SignedAccumulator Events []*Event // contains filtered or unexported fields }
Update contains all information for clients to update their witness to the latest accumulator: the accumulator itself and a set of revocation attributes. Its hash structure makes this message into a chain with the SignedAccumulator on top: The accumulator contains the hash of the first Event, and each Event has a hash of its parent. Thus, the signature over the accumulator effectively signs the entire Update message, and the partial tree specified by Events is verifiable regardless of its length.
func NewAccumulator ¶
func NewAccumulator(sk *gabikeys.PrivateKey) (*Update, error)
func NewUpdate ¶
func NewUpdate(sk *gabikeys.PrivateKey, acc *Accumulator, events []*Event) (*Update, error)
func (*Update) MarshalCBOR ¶
func (*Update) MarshalJSON ¶
func (*Update) UnmarshalCBOR ¶
func (*Update) UnmarshalJSON ¶
func (*Update) Verify ¶
func (update *Update) Verify(pk *gabikeys.PublicKey) (*Accumulator, error)
Verify that the specified update message is a validly signed partial chain: - the accumulator is validly signed - the accumulator includes the hash of the last item in the hash chain - the hash chain is valid (each chain item has the correct hash of its parent).
type Witness ¶
type Witness struct { // U^E = Accumulator.Nu mod N U *big.Int `json:"u"` E *big.Int `json:"e"` // Accumulator against which the witness verifies. SignedAccumulator *SignedAccumulator `json:"sacc"` // Update is set to now whenever the witness is updated, or when the RA indicates // there are no new updates. Thus, it specifies up to what time our nonrevocation // guarantees lasts. Updated time.Time // contains filtered or unexported fields }
Witness is a witness for the RSA-B accumulator, used for proving nonrevocation against the Accumulator with the same Index.
func RandomWitness ¶
func RandomWitness(sk *gabikeys.PrivateKey, acc *Accumulator) (*Witness, error)
RandomWitness returns a new random Witness valid against the specified Accumulator.