Commit 07ab5695 authored by Sietse Ringers's avatar Sietse Ringers
Browse files

feat: update nonrevocation cache on accumulator updates instead of discarding it

parent 709972d2
......@@ -336,11 +336,15 @@ func TestOptionalDisclosure(t *testing.T) {
}
}
func revocationSession(t *testing.T, client *irmaclient.Client, options ...sessionOption) *requestorSessionResult {
func revocationRequest() irma.SessionRequest {
attr := irma.NewAttributeTypeIdentifier("irma-demo.MijnOverheid.root.BSN")
req := irma.NewDisclosureRequest(attr)
req.Revocation = []irma.CredentialTypeIdentifier{attr.CredentialTypeIdentifier()}
result := requestorSessionHelper(t, req, client, options...)
return req
}
func revocationSession(t *testing.T, client *irmaclient.Client, options ...sessionOption) *requestorSessionResult {
result := requestorSessionHelper(t, revocationRequest(), client, options...)
require.Nil(t, result.Err)
return result
}
......@@ -373,6 +377,10 @@ func TestRevocation(t *testing.T) {
require.Equal(t, irma.ProofStatusValid, result.ProofStatus)
require.NotEmpty(t, result.Disclosed)
req := revocationRequest()
require.NoError(t, client.Configuration.RevocationStorage.SetRecords(req.Base()))
require.NoError(t, client.PrepareNonrevocation(req))
// revoke cred0
require.NoError(t, revocationServer.Revoke(cred, "cred0"))
......
......@@ -150,6 +150,10 @@ func New(
if schemeMgrErr != nil && !isSchemeMgrErr {
return nil, schemeMgrErr
}
cm.Configuration.RevocationPath = filepath.Join(storagePath, "revocation")
if err = fs.EnsureDirectoryExists(cm.Configuration.RevocationPath); err != nil {
return nil, err
}
// Ensure storage path exists, and populate it with necessary files
cm.storage = storage{storagePath: storagePath, Configuration: cm.Configuration}
......@@ -696,6 +700,55 @@ func (client *Client) groupCredentials(choice *irma.DisclosureChoice) (
return todisclose, attributeIndices, nil
}
// PrepareNonrevocation updates the revocation state for each credential in the request
// requiring a nonrevocation proof, using the updates included in the request, or the remote
// revocation server if those do not suffice.
func (client *Client) PrepareNonrevocation(request irma.SessionRequest) error {
var err error
var cred *credential
var updated bool
for id := range request.Disclosure().Identifiers().CredentialTypes {
typ := client.Configuration.CredentialTypes[id]
if !typ.SupportsRevocation() {
continue
}
for i := 0; i < len(client.attrs(id)); i++ {
if cred, err = client.credential(id, i); err != nil {
return err
}
if updated, err = cred.prepareNonrevocation(client.Configuration, request); err != nil {
return err
}
if updated {
if err = client.storage.StoreSignature(cred); err != nil {
return err
}
}
}
}
return nil
}
func (client *Client) repopulateNonrevCaches(request irma.SessionRequest) error {
var err error
var cred *credential
for id := range request.Disclosure().Identifiers().CredentialTypes {
typ := client.Configuration.CredentialTypes[id]
if !typ.SupportsRevocation() {
continue
}
for i := 0; i < len(client.attrs(id)); i++ {
if cred, err = client.credential(id, i); err != nil {
return err
}
if err = cred.PrepareNonrevCache(); err != nil {
return err
}
}
}
return nil
}
// ProofBuilders constructs a list of proof builders for the specified attribute choice.
func (client *Client) ProofBuilders(choice *irma.DisclosureChoice, request irma.SessionRequest,
) (gabi.ProofBuilderList, irma.DisclosedAttributeIndices, *atum.Timestamp, error) {
......@@ -711,15 +764,7 @@ func (client *Client) ProofBuilders(choice *irma.DisclosureChoice, request irma.
if err != nil {
return nil, nil, nil, err
}
nonrev, updated, err := cred.prepareNonrevocation(client.Configuration, request)
if err != nil {
return nil, nil, nil, err
}
if updated {
if err = client.storage.StoreSignature(cred); err != nil {
return nil, nil, nil, err
}
}
nonrev := request.Base().RequestsRevocation(cred.CredentialType().Identifier())
builder, err = cred.CreateDisclosureProofBuilder(grp.attrs, nonrev)
if err != nil {
return nil, nil, nil, err
......
......@@ -43,40 +43,32 @@ func (cred *credential) AttributeList() *irma.AttributeList {
// prepareNonrevocation attempts to update the credential's nonrevocation witness from
// 1) the session request, and then 2) the revocation server if our witness is too far out of date.
// Returns whether or not a nonrevocation proof should be included for this credential, and whether
// or not the credential's nonrevocation state was updated. If so the caller should persist the
// updated credential to storage.
func (cred *credential) prepareNonrevocation(conf *irma.Configuration, request irma.SessionRequest) (bool, bool, error) {
// If the requestor wants us to include a nonrevocation proof,
// it will have sent us the latest revocation update messages
m := request.Base().RevocationUpdates
// Returns whether or not the credential's nonrevocation state was updated. If so the caller should
// persist the updated credential to storage.
func (cred *credential) prepareNonrevocation(conf *irma.Configuration, request irma.SessionRequest) (bool, error) {
credtype := cred.CredentialType().Identifier()
if len(m) == 0 || len(m[credtype]) == 0 {
return false, false, nil
if !request.Base().RequestsRevocation(credtype) {
return false, nil
}
revupdates := m[credtype]
nonrev := len(revupdates) > 0
revupdates := request.Base().RevocationUpdates[credtype]
updated, err := cred.updateNonrevWitness(revupdates, conf.RevocationStorage)
if err != nil {
return false, updated, err
} else if updated {
cred.DiscardRevocationCache()
return updated, err
}
// TODO (in both branches): attach our newer updates to response
if nonrev && cred.NonRevocationWitness.Index >= revupdates[len(revupdates)-1].EndIndex {
return nonrev, updated, nil
if cred.NonRevocationWitness.Index >= revupdates[len(revupdates)-1].EndIndex {
return updated, nil
}
// nonrevocation witness is still out of date after applying the updates from the request,
// i.e. we were too far behind. Update from revocation server.
revupdates, err = conf.RevocationStorage.GetUpdates(credtype, cred.NonRevocationWitness.Index+1)
if err != nil {
return nonrev, updated, err
return updated, err
}
updated, err = cred.updateNonrevWitness(revupdates, conf.RevocationStorage)
return nonrev, updated, err
return cred.updateNonrevWitness(revupdates, conf.RevocationStorage)
}
// updateNonrevWitness updates the credential's nonrevocation witness using the specified messages,
......@@ -93,5 +85,6 @@ func (cred *credential) updateNonrevWitness(messages []*irma.RevocationRecord, r
return false, err
}
}
return cred.NonRevocationWitness.Index == oldindex, err
return cred.NonRevocationWitness.Index != oldindex, cred.PrepareNonrevCache()
}
......@@ -77,6 +77,7 @@ type session struct {
client *Client
request irma.SessionRequest
done bool
prepRevocation chan error
// State for issuance sessions
issuerProofNonce *big.Int
......@@ -143,6 +144,7 @@ func (client *Client) newManualSession(request irma.SessionRequest, handler Hand
client: client,
Version: minVersion,
request: request,
prepRevocation: make(chan error),
}
session.Handler.StatusUpdate(session.Action, irma.StatusManualStarted)
......@@ -187,6 +189,7 @@ func (client *Client) newQrSession(qr *irma.Qr, handler Handler) SessionDismisse
Action: irma.Action(qr.Type),
Handler: handler,
client: client,
prepRevocation: make(chan error),
}
session.Handler.StatusUpdate(session.Action, irma.StatusCommunicating)
......@@ -304,6 +307,11 @@ func (session *session) processSessionInfo() {
return
}
// Prepare and update all revocation state asynchroniously while the user makes her choices
go func() {
session.prepRevocation <- session.client.PrepareNonrevocation(session.request)
}()
// Ask for permission to execute the session
callback := PermissionHandler(func(proceed bool, choice *irma.DisclosureChoice) {
session.choice = choice
......@@ -343,6 +351,12 @@ func (session *session) doSession(proceed bool) {
}
session.Handler.StatusUpdate(session.Action, irma.StatusCommunicating)
// wait for revocation preparation to finish
err := <-session.prepRevocation
if err != nil {
session.fail(&irma.SessionError{ErrorType: irma.ErrorCrypto, Err: err}) // TODO error type
}
if !session.Distributed() {
message, err := session.getProof()
if err != nil {
......@@ -455,6 +469,13 @@ func (session *session) sendResponse(message interface{}) {
}
session.done = true
session.Handler.Success(string(messageJson))
go func() {
if err := session.client.repopulateNonrevCaches(session.request); err != nil {
raven.CaptureError(err, nil)
irma.Logger.Error(err)
}
}()
}
// managerSession performs a "session" in which a new scheme manager is added (asking for permission first).
......
......@@ -229,6 +229,12 @@ func (b *BaseRequest) GetNonce(*atum.Timestamp) *big.Int {
return b.Nonce
}
func (b *BaseRequest) RequestsRevocation(id CredentialTypeIdentifier) bool {
// If the requestor wants us to include a nonrevocation proof,
// it will have sent us the latest revocation update messages
return len(b.RevocationUpdates) > 0 && len(b.RevocationUpdates[id]) > 0
}
const revocationUpdateCount = 5
// CredentialTypes returns an array of all credential types occuring in this conjunction.
......
......@@ -72,7 +72,7 @@ func (rdb *DB) EnableRevocation(sk *revocation.PrivateKey) error {
if err = rdb.Add(msg, sk.Counter); err != nil {
return err
}
rdb.Current = acc
rdb.Current = *acc
return nil
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment