Commit 669bd187 authored by Sietse Ringers's avatar Sietse Ringers
Browse files

feat: refactor revocation update messages into hashed chain structure

parent 33598603
package irma package irma
import ( import (
"database/sql/driver" "database/sql/driver" // only imported to refer to the driver.Value type
"fmt" "fmt"
"strings" "strings"
......
...@@ -14,6 +14,7 @@ import ( ...@@ -14,6 +14,7 @@ import (
"github.com/go-errors/errors" "github.com/go-errors/errors"
"github.com/jasonlvhit/gocron" "github.com/jasonlvhit/gocron"
"github.com/privacybydesign/gabi/revocation"
"github.com/privacybydesign/irmago" "github.com/privacybydesign/irmago"
"github.com/privacybydesign/irmago/server" "github.com/privacybydesign/irmago/server"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
...@@ -154,7 +155,7 @@ func (s *Server) Revoke(credid irma.CredentialTypeIdentifier, key string) error ...@@ -154,7 +155,7 @@ func (s *Server) Revoke(credid irma.CredentialTypeIdentifier, key string) error
} }
func ParsePath(path string) (token, noun string, arg []string, err error) { func ParsePath(path string) (token, noun string, arg []string, err error) {
rev := regexp.MustCompile("revocation/(records|latestrecords|issuancerecord)/?(.*)$") rev := regexp.MustCompile("revocation/(updatefrom|updatelatest|update|issuancerecord)/?(.*)$")
matches := rev.FindStringSubmatch(path) matches := rev.FindStringSubmatch(path)
if len(matches) == 3 { if len(matches) == 3 {
args := strings.Split(matches[2], "/") args := strings.Split(matches[2], "/")
...@@ -381,7 +382,7 @@ func (s *Server) handleClientMessage( ...@@ -381,7 +382,7 @@ func (s *Server) handleClientMessage(
func (s *Server) handleRevocationMessage( func (s *Server) handleRevocationMessage(
noun, method string, args []string, headers map[string][]string, message []byte, noun, method string, args []string, headers map[string][]string, message []byte,
) (int, []byte) { ) (int, []byte) {
if (noun == "records" || noun == "latestrecords") && method == http.MethodGet { if (noun == "updatefrom" || noun == "updatelatest") && method == http.MethodGet {
if len(args) != 2 { if len(args) != 2 {
return server.JsonResponse(nil, server.RemoteError(server.ErrorInvalidRequest, "GET "+noun+" expects 2 url arguments")) return server.JsonResponse(nil, server.RemoteError(server.ErrorInvalidRequest, "GET "+noun+" expects 2 url arguments"))
} }
...@@ -390,21 +391,22 @@ func (s *Server) handleRevocationMessage( ...@@ -390,21 +391,22 @@ func (s *Server) handleRevocationMessage(
return server.JsonResponse(nil, server.RemoteError(server.ErrorMalformedInput, err.Error())) return server.JsonResponse(nil, server.RemoteError(server.ErrorMalformedInput, err.Error()))
} }
cred := irma.NewCredentialTypeIdentifier(args[0]) cred := irma.NewCredentialTypeIdentifier(args[0])
if noun == "records" { if noun == "updatefrom" {
return server.JsonResponse(s.handleGetRevocationRecords(cred, i)) return server.JsonResponse(s.handleGetUpdateFrom(cred, i))
} else { } else {
return server.JsonResponse(s.handleGetLatestRevocationRecords(cred, i)) return server.JsonResponse(s.handleGetUpdateLatest(cred, i))
} }
} }
if noun == "records" && method == http.MethodPost { if noun == "update" && method == http.MethodPost {
if len(args) != 0 { if len(args) != 1 {
return server.JsonResponse(nil, server.RemoteError(server.ErrorInvalidRequest, "POST records expects no url arguments")) return server.JsonResponse(nil, server.RemoteError(server.ErrorInvalidRequest, "POST update expects 1 url argument"))
} }
var records []*irma.RevocationRecord cred := irma.NewCredentialTypeIdentifier(args[0])
if err := json.Unmarshal(message, &records); err != nil { var update *revocation.Update
if err := json.Unmarshal(message, &update); err != nil {
return server.JsonResponse(nil, server.RemoteError(server.ErrorMalformedInput, err.Error())) return server.JsonResponse(nil, server.RemoteError(server.ErrorMalformedInput, err.Error()))
} }
return server.JsonResponse(s.handlePostRevocationRecords(records)) return server.JsonResponse(s.handlePostUpdate(cred, update))
} }
if noun == "issuancerecord" && method == http.MethodPost { if noun == "issuancerecord" && method == http.MethodPost {
if len(args) != 2 { if len(args) != 2 {
......
...@@ -4,6 +4,7 @@ import ( ...@@ -4,6 +4,7 @@ import (
"time" "time"
"github.com/privacybydesign/gabi" "github.com/privacybydesign/gabi"
"github.com/privacybydesign/gabi/revocation"
"github.com/privacybydesign/gabi/signed" "github.com/privacybydesign/gabi/signed"
"github.com/privacybydesign/irmago" "github.com/privacybydesign/irmago"
"github.com/privacybydesign/irmago/server" "github.com/privacybydesign/irmago/server"
...@@ -33,10 +34,10 @@ func (session *session) handleGetRequest(min, max *irma.ProtocolVersion) (irma.S ...@@ -33,10 +34,10 @@ func (session *session) handleGetRequest(min, max *irma.ProtocolVersion) (irma.S
session.markAlive() session.markAlive()
logger := session.conf.Logger.WithFields(logrus.Fields{"session": session.token}) logger := session.conf.Logger.WithFields(logrus.Fields{"session": session.token})
// we include the latest revocation records for the client here, as opposed to when the session // we include the latest revocation updates for the client here, as opposed to when the session
// was started, so that the client always gets the very latest revocation records // was started, so that the client always gets the very latest revocation records
var err error var err error
if err = session.conf.IrmaConfiguration.Revocation.SetRevocationRecords(session.request.Base()); err != nil { if err = session.conf.IrmaConfiguration.Revocation.SetRevocationUpdates(session.request.Base()); err != nil {
return nil, session.fail(server.ErrorUnknown, err.Error()) // TODO error type return nil, session.fail(server.ErrorUnknown, err.Error()) // TODO error type
} }
...@@ -214,40 +215,42 @@ func (session *session) handlePostCommitments(commitments *irma.IssueCommitmentM ...@@ -214,40 +215,42 @@ func (session *session) handlePostCommitments(commitments *irma.IssueCommitmentM
return sigs, nil return sigs, nil
} }
// POST revocation/records // POST revocation/update/{credtype}
func (s *Server) handlePostRevocationRecords(records []*irma.RevocationRecord) (interface{}, *irma.RemoteError) { func (s *Server) handlePostUpdate(typ irma.CredentialTypeIdentifier, update *revocation.Update) (interface{}, *irma.RemoteError) {
if err := s.conf.IrmaConfiguration.Revocation.AddRevocationRecords(records); err != nil { if err := s.conf.IrmaConfiguration.Revocation.AddUpdate(typ, update); err != nil {
return nil, server.RemoteError(server.ErrorUnknown, err.Error()) // TODO error type return nil, server.RemoteError(server.ErrorUnknown, err.Error()) // TODO error type
} }
return nil, nil return nil, nil
} }
// GET revocation/records/{credtype}/{index} // GET revocation/updatefrom/{credtype}/{index}
func (s *Server) handleGetRevocationRecords( func (s *Server) handleGetUpdateFrom(
cred irma.CredentialTypeIdentifier, index uint64, cred irma.CredentialTypeIdentifier, index uint64,
) ([]*irma.RevocationRecord, *irma.RemoteError) { ) (*revocation.Update, *irma.RemoteError) {
if _, ok := s.conf.RevocationSettings[cred]; !ok { if settings := s.conf.RevocationSettings[cred]; settings == nil ||
!(settings.Mode == irma.RevocationModeProxy || settings.Mode == irma.RevocationModeServer) {
return nil, server.RemoteError(server.ErrorInvalidRequest, "not supported by this server") return nil, server.RemoteError(server.ErrorInvalidRequest, "not supported by this server")
} }
records, err := s.conf.IrmaConfiguration.Revocation.RevocationRecords(cred, index) update, err := s.conf.IrmaConfiguration.Revocation.UpdateFrom(cred, index)
if err != nil { if err != nil {
return nil, server.RemoteError(server.ErrorUnknown, err.Error()) // TODO error type return nil, server.RemoteError(server.ErrorUnknown, err.Error()) // TODO error type
} }
return records, nil return update, nil
} }
// GET revocation/latestrecords/{credtype}/{count} // GET revocation/updatelatest/{credtype}/{count}
func (s *Server) handleGetLatestRevocationRecords( func (s *Server) handleGetUpdateLatest(
cred irma.CredentialTypeIdentifier, count uint64, cred irma.CredentialTypeIdentifier, count uint64,
) ([]*irma.RevocationRecord, *irma.RemoteError) { ) (*revocation.Update, *irma.RemoteError) {
if _, ok := s.conf.RevocationSettings[cred]; !ok { if settings := s.conf.RevocationSettings[cred]; settings == nil ||
!(settings.Mode == irma.RevocationModeProxy || settings.Mode == irma.RevocationModeServer) {
return nil, server.RemoteError(server.ErrorInvalidRequest, "not supported by this server") return nil, server.RemoteError(server.ErrorInvalidRequest, "not supported by this server")
} }
records, err := s.conf.IrmaConfiguration.Revocation.LatestRevocationRecords(cred, count) update, err := s.conf.IrmaConfiguration.Revocation.UpdateLatest(cred, count)
if err != nil { if err != nil {
return nil, server.RemoteError(server.ErrorUnknown, err.Error()) // TODO error type return nil, server.RemoteError(server.ErrorUnknown, err.Error()) // TODO error type
} }
return records, nil return update, nil
} }
// POST revocation/issuancerecord/{credtype}/{keycounter} // POST revocation/issuancerecord/{credtype}/{keycounter}
......
...@@ -95,25 +95,25 @@ func (session *session) issuanceHandleRevocation( ...@@ -95,25 +95,25 @@ func (session *session) issuanceHandleRevocation(
// Fetch latest revocation record, and then extract the current value of the accumulator // Fetch latest revocation record, and then extract the current value of the accumulator
// from it to generate the witness from // from it to generate the witness from
records, err := rs.LatestRevocationRecords(id, 1) u, err := rs.UpdateLatest(id, 0)
if err != nil { if err != nil {
return return
} }
r := records[len(records)-1] sig := u.SignedAccumulator
pk, err := rs.Keys.PublicKey(id.IssuerIdentifier(), r.PublicKeyIndex) pk, err := rs.Keys.PublicKey(id.IssuerIdentifier(), sig.PKIndex)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
msg, err := r.UnmarshalVerify(pk) acc, err := sig.UnmarshalVerify(pk)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
if witness, err = sk.RevocationGenerateWitness(&msg.Accumulator); err != nil { if witness, err = sk.RevocationGenerateWitness(acc); err != nil {
return return
} }
witness.Record = &r.Record // attach previously selected reocation record to the witness for the client witness.SignedAccumulator = sig // attach previously selected reocation record to the witness for the client
nonrevAttr = witness.E nonrevAttr = witness.E
issrecord := &irma.IssuanceRecord{ issrecord := &irma.IssuanceRecord{
CredType: id, CredType: id,
...@@ -122,7 +122,7 @@ func (session *session) issuanceHandleRevocation( ...@@ -122,7 +122,7 @@ func (session *session) issuanceHandleRevocation(
Issued: time.Now().UnixNano(), // or (floored) cred issuance time? Issued: time.Now().UnixNano(), // or (floored) cred issuance time?
ValidUntil: attributes.Expiry().UnixNano(), ValidUntil: attributes.Expiry().UnixNano(),
} }
err = session.conf.IrmaConfiguration.Revocation.SaveIssuanceRecord(id, issrecord) err = session.conf.IrmaConfiguration.Revocation.SaveIssuanceRecord(id, issrecord, sk)
return return
} }
......
...@@ -5,7 +5,6 @@ import ( ...@@ -5,7 +5,6 @@ import (
"encoding/json" "encoding/json"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"reflect" "reflect"
"testing" "testing"
......
...@@ -83,9 +83,11 @@ func StartRevocationServer(t *testing.T) { ...@@ -83,9 +83,11 @@ func StartRevocationServer(t *testing.T) {
// Connect to database and clear records from previous test runs // Connect to database and clear records from previous test runs
g, err := gorm.Open("postgres", conf.RevocationDB) g, err := gorm.Open("postgres", conf.RevocationDB)
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, g.DropTableIfExists((*irma.RevocationRecord)(nil)).Error) require.NoError(t, g.DropTableIfExists((*irma.EventRecord)(nil)).Error)
require.NoError(t, g.DropTableIfExists((*irma.AccumulatorRecord)(nil)).Error)
require.NoError(t, g.DropTableIfExists((*irma.IssuanceRecord)(nil)).Error) require.NoError(t, g.DropTableIfExists((*irma.IssuanceRecord)(nil)).Error)
require.NoError(t, g.AutoMigrate((*irma.RevocationRecord)(nil)).Error) require.NoError(t, g.AutoMigrate((*irma.EventRecord)(nil)).Error)
require.NoError(t, g.AutoMigrate((*irma.AccumulatorRecord)(nil)).Error)
require.NoError(t, g.AutoMigrate((*irma.IssuanceRecord)(nil)).Error) require.NoError(t, g.AutoMigrate((*irma.IssuanceRecord)(nil)).Error)
require.NoError(t, g.Close()) require.NoError(t, g.Close())
......
package irmaclient package irmaclient
import ( import (
"errors"
"github.com/privacybydesign/gabi" "github.com/privacybydesign/gabi"
"github.com/privacybydesign/gabi/revocation" "github.com/privacybydesign/gabi/revocation"
"github.com/privacybydesign/irmago" "github.com/privacybydesign/irmago"
...@@ -61,17 +59,18 @@ func (cred *credential) NonrevPrepare(conf *irma.Configuration, request irma.Ses ...@@ -61,17 +59,18 @@ func (cred *credential) NonrevPrepare(conf *irma.Configuration, request irma.Ses
// first try to update witness by applying the revocation update messages attached to the session request // first try to update witness by applying the revocation update messages attached to the session request
keys := irma.RevocationKeys{Conf: conf} keys := irma.RevocationKeys{Conf: conf}
revupdates := base.RevocationUpdates[credtype] revupdates := base.RevocationUpdates[credtype]
count := len(revupdates.Events)
updated, err := cred.NonrevApplyUpdates(revupdates, keys) updated, err := cred.NonrevApplyUpdates(revupdates, keys)
if err != nil { if err != nil {
return updated, err return updated, err
} }
if cred.NonRevocationWitness.Accumulator.Index >= revupdates[len(revupdates)-1].EndIndex { if cred.NonRevocationWitness.Accumulator.Index >= revupdates.Events[count-1].Index {
return updated, nil return updated, nil
} }
// nonrevocation witness is still out of date after applying the updates from the request: // nonrevocation witness is still out of date after applying the updates from the request:
// we were too far behind. Update from revocation server. // we were too far behind. Update from revocation server.
revupdates, err = irma.RevocationClient{Conf: conf}.FetchRevocationRecords(credtype, cred.NonRevocationWitness.Accumulator.Index+1) revupdates, err = irma.RevocationClient{Conf: conf}.FetchUpdateFrom(credtype, cred.NonRevocationWitness.Accumulator.Index+1)
if err != nil { if err != nil {
return updated, err return updated, err
} }
...@@ -80,21 +79,15 @@ func (cred *credential) NonrevPrepare(conf *irma.Configuration, request irma.Ses ...@@ -80,21 +79,15 @@ func (cred *credential) NonrevPrepare(conf *irma.Configuration, request irma.Ses
// NonrevApplyUpdates updates the credential's nonrevocation witness using the specified messages, // NonrevApplyUpdates updates the credential's nonrevocation witness using the specified messages,
// if they all verify and if their indices are ahead and adjacent to that of our witness. // if they all verify and if their indices are ahead and adjacent to that of our witness.
func (cred *credential) NonrevApplyUpdates(messages []*irma.RevocationRecord, keys irma.RevocationKeys) (bool, error) { func (cred *credential) NonrevApplyUpdates(update *revocation.Update, keys irma.RevocationKeys) (bool, error) {
oldindex := cred.NonRevocationWitness.Accumulator.Index oldindex := cred.NonRevocationWitness.Accumulator.Index
var err error pk, err := keys.PublicKey(cred.CredentialType().IssuerIdentifier(), update.SignedAccumulator.PKIndex)
var pk *revocation.PublicKey if err != nil {
for _, record := range messages { return false, err
if cred.CredentialType().IssuerIdentifier() != record.CredType.IssuerIdentifier() { }
return false, errors.New("cannot apply revocation record of other credential type") if err = cred.NonRevocationWitness.Update(pk, update); err != nil {
} return false, err
if pk, err = keys.PublicKey(cred.CredentialType().IssuerIdentifier(), record.PublicKeyIndex); err != nil {
return false, err
}
if err = cred.NonRevocationWitness.Update(pk, &record.Record); err != nil {
return false, err
}
} }
return cred.NonRevocationWitness.Accumulator.Index != oldindex, nil return cred.NonRevocationWitness.Accumulator.Index != oldindex, nil
......
...@@ -359,6 +359,7 @@ func (session *session) doSession(proceed bool) { ...@@ -359,6 +359,7 @@ func (session *session) doSession(proceed bool) {
err := <-session.prepRevocation err := <-session.prepRevocation
if err != nil { if err != nil {
session.fail(&irma.SessionError{ErrorType: irma.ErrorCrypto, Err: err}) // TODO error type session.fail(&irma.SessionError{ErrorType: irma.ErrorCrypto, Err: err}) // TODO error type
return
} }
if !session.Distributed() { if !session.Distributed() {
...@@ -668,6 +669,7 @@ func (session *session) finish() bool { ...@@ -668,6 +669,7 @@ func (session *session) finish() bool {
func (session *session) fail(err *irma.SessionError) { func (session *session) fail(err *irma.SessionError) {
if session.finish() && err.ErrorType != irma.ErrorKeyshareUnenrolled { if session.finish() && err.ErrorType != irma.ErrorKeyshareUnenrolled {
irma.Logger.Warn("client session error: ", err.Error())
err.Err = errors.Wrap(err.Err, 0) err.Err = errors.Wrap(err.Err, 0)
session.Handler.Failure(err) session.Handler.Failure(err)
} }
......
...@@ -254,7 +254,7 @@ func (s *storage) LoadSignature(attrs *irma.AttributeList) (*gabi.CLSignature, * ...@@ -254,7 +254,7 @@ func (s *storage) LoadSignature(attrs *irma.AttributeList) (*gabi.CLSignature, *
if sig.Witness != nil { if sig.Witness != nil {
pk, err := s.Configuration.Revocation.Keys.PublicKey( pk, err := s.Configuration.Revocation.Keys.PublicKey(
attrs.CredentialType().IssuerIdentifier(), attrs.CredentialType().IssuerIdentifier(),
sig.Witness.Record.PublicKeyIndex, sig.Witness.SignedAccumulator.PKIndex,
) )
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
......
...@@ -13,6 +13,7 @@ import ( ...@@ -13,6 +13,7 @@ import (
"github.com/go-errors/errors" "github.com/go-errors/errors"
"github.com/privacybydesign/gabi" "github.com/privacybydesign/gabi"
"github.com/privacybydesign/gabi/big" "github.com/privacybydesign/gabi/big"
"github.com/privacybydesign/gabi/revocation"
"github.com/privacybydesign/irmago/internal/fs" "github.com/privacybydesign/irmago/internal/fs"
) )
...@@ -38,7 +39,7 @@ type BaseRequest struct { ...@@ -38,7 +39,7 @@ type BaseRequest struct {
Revocation []CredentialTypeIdentifier `json:"revocation,omitempty"` Revocation []CredentialTypeIdentifier `json:"revocation,omitempty"`
// RevocationUpdates contains revocation update messages for the client to update its // RevocationUpdates contains revocation update messages for the client to update its
// revocation state. // revocation state.
RevocationUpdates map[CredentialTypeIdentifier][]*RevocationRecord `json:"revocationUpdates,omitempty"` RevocationUpdates map[CredentialTypeIdentifier]*revocation.Update `json:"revocationUpdates,omitempty"`
ids *IrmaIdentifierSet // cache for Identifiers() method ids *IrmaIdentifierSet // cache for Identifiers() method
...@@ -199,7 +200,7 @@ type AttributeRequest struct { ...@@ -199,7 +200,7 @@ type AttributeRequest struct {
type RevocationRequest struct { type RevocationRequest struct {
LDContext string `json:"@context,omitempty"` LDContext string `json:"@context,omitempty"`
CredentialType CredentialTypeIdentifier `json:"type"` CredentialType CredentialTypeIdentifier `json:"type"`
Key string `json:"key,omitempty"` Key string `json:"revocationKey,omitempty"`
Enable bool `json:"enable,omitempty"` Enable bool `json:"enable,omitempty"`
} }
...@@ -236,7 +237,7 @@ func (b *BaseRequest) GetNonce(*atum.Timestamp) *big.Int { ...@@ -236,7 +237,7 @@ func (b *BaseRequest) GetNonce(*atum.Timestamp) *big.Int {
// RequestsRevocation indicates whether or not the requestor requires a nonrevocation proof for // RequestsRevocation indicates whether or not the requestor requires a nonrevocation proof for
// the given credential type; that is, whether or not it included revocation update messages. // the given credential type; that is, whether or not it included revocation update messages.
func (b *BaseRequest) RequestsRevocation(id CredentialTypeIdentifier) bool { func (b *BaseRequest) RequestsRevocation(id CredentialTypeIdentifier) bool {
return len(b.RevocationUpdates) > 0 && len(b.RevocationUpdates[id]) > 0 return len(b.RevocationUpdates) > 0 && len(b.RevocationUpdates[id].Events) > 0
} }
func (b *BaseRequest) RevocationConsistent() error { func (b *BaseRequest) RevocationConsistent() error {
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/postgres" _ "github.com/jinzhu/gorm/dialects/postgres"
"github.com/privacybydesign/gabi"
"github.com/privacybydesign/gabi/big" "github.com/privacybydesign/gabi/big"
"github.com/privacybydesign/gabi/revocation" "github.com/privacybydesign/gabi/revocation"
"github.com/privacybydesign/gabi/signed" "github.com/privacybydesign/gabi/signed"
...@@ -41,13 +42,13 @@ type ( ...@@ -41,13 +42,13 @@ type (
// RevocationSetting contains revocation settings for a given credential type. // RevocationSetting contains revocation settings for a given credential type.
RevocationSetting struct { RevocationSetting struct {
Mode RevocationMode `json:"mode"` Mode RevocationMode `json:"mode" mapstructure:"mode"`
PostURLs []string `json:"post_urls" mapstructure:"post_urls"` PostURLs []string `json:"post_urls" mapstructure:"post_urls"`
MaxNonrevocationDuration uint `json:"max_nonrev_duration" mapstructure:"max_nonrev_duration"` // in seconds, min 30
ServerURL string `json:"server_url" mapstructure:"server_url"` ServerURL string `json:"server_url" mapstructure:"server_url"`
MaxNonrevocationDuration uint `json:"max_nonrev_duration" mapstructure:"max_nonrev_duration"` // in seconds, min 30
// set to now whenever a new revocation record is received, or when the RA indicates // set to now whenever a new update is received, or when the RA indicates
// there are no new records. Thus it specifies up to what time our nonrevocation // there are no new updates. Thus it specifies up to what time our nonrevocation
// guarantees lasts. // guarantees lasts.
updated time.Time updated time.Time
} }
...@@ -55,18 +56,18 @@ type ( ...@@ -55,18 +56,18 @@ type (
// RevocationMode specifies for a given credential type what revocation operations are // RevocationMode specifies for a given credential type what revocation operations are
// supported, and how the associated data is stored (SQL or memory). // supported, and how the associated data is stored (SQL or memory).
RevocationMode string RevocationMode string
)
// RevocationRecord contains a signed AccumulatorUpdate and associated information and is used // Structs corresponding to SQL table rows. All of them end in Record.
// by clients, issuers and verifiers to update their revocation state, so that they can create type (
// and verify nonrevocation proofs and witnesses. AccumulatorRecord struct {
RevocationRecord struct { *revocation.SignedAccumulator `gorm:"embedded"`
revocation.Record `gorm:"embedded"` CredType CredentialTypeIdentifier `gorm:"primary_key"`
CredType CredentialTypeIdentifier `gorm:"primary_key"`
} }
TimeRecord struct { EventRecord struct {
Index uint64 *revocation.Event `gorm:"embedded"`
Start, End int64 CredType CredentialTypeIdentifier `gorm:"primary_key"`
} }
// IssuanceRecord contains information generated during issuance, needed for later revocation. // IssuanceRecord contains information generated during issuance, needed for later revocation.
...@@ -78,6 +79,12 @@ type ( ...@@ -78,6 +79,12 @@ type (
ValidUntil int64 ValidUntil int64
RevokedAt int64 // 0 if not currently revoked RevokedAt int64 // 0 if not currently revoked
} }
// TODO
TimeRecord struct {
Index uint64
Start, End int64
}
) )
const ( const (
...@@ -86,21 +93,21 @@ const ( ...@@ -86,21 +93,21 @@ const (
RevocationModeRequestor RevocationMode = "" RevocationModeRequestor RevocationMode = ""
// RevocationModeProxy indicates that this server // RevocationModeProxy indicates that this server
// (1) allows fetching of RevocationRecord instances from its database, // (1) allows fetching of revocation update messages from its database,
// (2) relays all RevocationRecord instances it receives to the URLs configured in the containing // (2) relays all revocation updates it receives to the URLs configured in the containing
// RevocationSetting struct. // RevocationSetting struct.
// Requires a SQL server to store and retrieve RevocationRecord instances from. // Requires a SQL server to store and retrieve update messages from.
RevocationModeProxy RevocationMode = "proxy" RevocationModeProxy RevocationMode = "proxy"
// RevocationModeServer indicates that this is a revocation server for a credential type. // RevocationModeServer indicates that this is a revocation server for a credential type.
// IssuanceRecord instances are sent to this server, as well as revocation commands, through // IssuanceRecord instances are sent to this server, as well as revocation commands, through
// revocation sessions or through the RevocationStorage.Revoke() method. // revocation sessions or through the RevocationStorage.Revoke() method.
// Requires a SQL server to store and retrieve all records from and requires the issuer's // Requires a SQL server to store and retrieve all records from and requires the issuer's
// private key to be accessible, in order to revoke and to sign new revocation records. // private key to be accessible, in order to revoke and to sign new revocation update messages.
// In addition this mode exposes the same endpoints as RevocationModeProxy. // In addition this mode exposes the same endpoints as RevocationModeProxy.
RevocationModeServer RevocationMode = "server" RevocationModeServer RevocationMode = "server"
// revocationUpdateCount specifies how many revocation records are attached to session requests // revocationUpdateCount specifies how many revocation events are attached to session requests
// for the client to update its revocation state. // for the client to update its revocation state.
revocationUpdateCount = 5 revocationUpdateCount = 5
...@@ -111,35 +118,24 @@ const ( ...@@ -111,35 +118,24 @@ const (
revocationMaxAccumulatorAge uint = 5 * 60 revocationMaxAccumulatorAge uint = 5 * 60
) )
// Revocation record methods
// EnableRevocation creates an initial accumulator for a given credential type. This function is the // EnableRevocation creates an initial accumulator for a given credential type. This function is the
// only way to create such an initial accumulator and it must be called before anyone can use // only way to create such an initial accumulator and it must be called before anyone can use