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) { ...@@ -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") attr := irma.NewAttributeTypeIdentifier("irma-demo.MijnOverheid.root.BSN")
req := irma.NewDisclosureRequest(attr) req := irma.NewDisclosureRequest(attr)
req.Revocation = []irma.CredentialTypeIdentifier{attr.CredentialTypeIdentifier()} 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) require.Nil(t, result.Err)
return result return result
} }
...@@ -373,6 +377,10 @@ func TestRevocation(t *testing.T) { ...@@ -373,6 +377,10 @@ func TestRevocation(t *testing.T) {
require.Equal(t, irma.ProofStatusValid, result.ProofStatus) require.Equal(t, irma.ProofStatusValid, result.ProofStatus)
require.NotEmpty(t, result.Disclosed) require.NotEmpty(t, result.Disclosed)
req := revocationRequest()
require.NoError(t, client.Configuration.RevocationStorage.SetRecords(req.Base()))
require.NoError(t, client.PrepareNonrevocation(req))
// revoke cred0 // revoke cred0
require.NoError(t, revocationServer.Revoke(cred, "cred0")) require.NoError(t, revocationServer.Revoke(cred, "cred0"))
......
...@@ -150,6 +150,10 @@ func New( ...@@ -150,6 +150,10 @@ func New(
if schemeMgrErr != nil && !isSchemeMgrErr { if schemeMgrErr != nil && !isSchemeMgrErr {
return nil, schemeMgrErr 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 // Ensure storage path exists, and populate it with necessary files
cm.storage = storage{storagePath: storagePath, Configuration: cm.Configuration} cm.storage = storage{storagePath: storagePath, Configuration: cm.Configuration}
...@@ -696,6 +700,55 @@ func (client *Client) groupCredentials(choice *irma.DisclosureChoice) ( ...@@ -696,6 +700,55 @@ func (client *Client) groupCredentials(choice *irma.DisclosureChoice) (
return todisclose, attributeIndices, nil 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. // ProofBuilders constructs a list of proof builders for the specified attribute choice.
func (client *Client) ProofBuilders(choice *irma.DisclosureChoice, request irma.SessionRequest, func (client *Client) ProofBuilders(choice *irma.DisclosureChoice, request irma.SessionRequest,
) (gabi.ProofBuilderList, irma.DisclosedAttributeIndices, *atum.Timestamp, error) { ) (gabi.ProofBuilderList, irma.DisclosedAttributeIndices, *atum.Timestamp, error) {
...@@ -711,15 +764,7 @@ func (client *Client) ProofBuilders(choice *irma.DisclosureChoice, request irma. ...@@ -711,15 +764,7 @@ func (client *Client) ProofBuilders(choice *irma.DisclosureChoice, request irma.
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
nonrev, updated, err := cred.prepareNonrevocation(client.Configuration, request) nonrev := request.Base().RequestsRevocation(cred.CredentialType().Identifier())
if err != nil {
return nil, nil, nil, err
}
if updated {
if err = client.storage.StoreSignature(cred); err != nil {
return nil, nil, nil, err
}
}
builder, err = cred.CreateDisclosureProofBuilder(grp.attrs, nonrev) builder, err = cred.CreateDisclosureProofBuilder(grp.attrs, nonrev)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
......
...@@ -43,40 +43,32 @@ func (cred *credential) AttributeList() *irma.AttributeList { ...@@ -43,40 +43,32 @@ func (cred *credential) AttributeList() *irma.AttributeList {
// prepareNonrevocation attempts to update the credential's nonrevocation witness from // 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. // 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 // Returns whether or not the credential's nonrevocation state was updated. If so the caller should
// or not the credential's nonrevocation state was updated. If so the caller should persist the // persist the updated credential to storage.
// updated credential to storage. func (cred *credential) prepareNonrevocation(conf *irma.Configuration, request irma.SessionRequest) (bool, error) {
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
credtype := cred.CredentialType().Identifier() credtype := cred.CredentialType().Identifier()
if len(m) == 0 || len(m[credtype]) == 0 { if !request.Base().RequestsRevocation(credtype) {
return false, false, nil return false, nil
} }
revupdates := m[credtype] revupdates := request.Base().RevocationUpdates[credtype]
nonrev := len(revupdates) > 0
updated, err := cred.updateNonrevWitness(revupdates, conf.RevocationStorage) updated, err := cred.updateNonrevWitness(revupdates, conf.RevocationStorage)
if err != nil { if err != nil {
return false, updated, err return updated, err
} else if updated {
cred.DiscardRevocationCache()
} }
// TODO (in both branches): attach our newer updates to response // TODO (in both branches): attach our newer updates to response
if nonrev && cred.NonRevocationWitness.Index >= revupdates[len(revupdates)-1].EndIndex { if cred.NonRevocationWitness.Index >= revupdates[len(revupdates)-1].EndIndex {
return nonrev, 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,
// i.e. we were too far behind. Update from revocation server. // i.e. we were too far behind. Update from revocation server.
revupdates, err = conf.RevocationStorage.GetUpdates(credtype, cred.NonRevocationWitness.Index+1) revupdates, err = conf.RevocationStorage.GetUpdates(credtype, cred.NonRevocationWitness.Index+1)
if err != nil { if err != nil {
return nonrev, updated, err return updated, err
} }
updated, err = cred.updateNonrevWitness(revupdates, conf.RevocationStorage) return cred.updateNonrevWitness(revupdates, conf.RevocationStorage)
return nonrev, updated, err
} }
// updateNonrevWitness updates the credential's nonrevocation witness using the specified messages, // updateNonrevWitness updates the credential's nonrevocation witness using the specified messages,
...@@ -93,5 +85,6 @@ func (cred *credential) updateNonrevWitness(messages []*irma.RevocationRecord, r ...@@ -93,5 +85,6 @@ func (cred *credential) updateNonrevWitness(messages []*irma.RevocationRecord, r
return false, err return false, err
} }
} }
return cred.NonRevocationWitness.Index == oldindex, err
return cred.NonRevocationWitness.Index != oldindex, cred.PrepareNonrevCache()
} }
...@@ -72,11 +72,12 @@ type session struct { ...@@ -72,11 +72,12 @@ type session struct {
Version *irma.ProtocolVersion Version *irma.ProtocolVersion
ServerName irma.TranslatedString ServerName irma.TranslatedString
choice *irma.DisclosureChoice choice *irma.DisclosureChoice
attrIndices irma.DisclosedAttributeIndices attrIndices irma.DisclosedAttributeIndices
client *Client client *Client
request irma.SessionRequest request irma.SessionRequest
done bool done bool
prepRevocation chan error
// State for issuance sessions // State for issuance sessions
issuerProofNonce *big.Int issuerProofNonce *big.Int
...@@ -138,11 +139,12 @@ func (client *Client) NewSession(sessionrequest string, handler Handler) Session ...@@ -138,11 +139,12 @@ func (client *Client) NewSession(sessionrequest string, handler Handler) Session
// newManualSession starts a manual session, given a signature request in JSON and a handler to pass messages to // newManualSession starts a manual session, given a signature request in JSON and a handler to pass messages to
func (client *Client) newManualSession(request irma.SessionRequest, handler Handler, action irma.Action) SessionDismisser { func (client *Client) newManualSession(request irma.SessionRequest, handler Handler, action irma.Action) SessionDismisser {
session := &session{ session := &session{
Action: action, Action: action,
Handler: handler, Handler: handler,
client: client, client: client,
Version: minVersion, Version: minVersion,
request: request, request: request,
prepRevocation: make(chan error),
} }
session.Handler.StatusUpdate(session.Action, irma.StatusManualStarted) session.Handler.StatusUpdate(session.Action, irma.StatusManualStarted)
...@@ -181,12 +183,13 @@ func (client *Client) newQrSession(qr *irma.Qr, handler Handler) SessionDismisse ...@@ -181,12 +183,13 @@ func (client *Client) newQrSession(qr *irma.Qr, handler Handler) SessionDismisse
u, _ := url.ParseRequestURI(qr.URL) // Qr validator already checked this for errors u, _ := url.ParseRequestURI(qr.URL) // Qr validator already checked this for errors
session := &session{ session := &session{
ServerURL: qr.URL, ServerURL: qr.URL,
Hostname: u.Hostname(), Hostname: u.Hostname(),
transport: irma.NewHTTPTransport(qr.URL), transport: irma.NewHTTPTransport(qr.URL),
Action: irma.Action(qr.Type), Action: irma.Action(qr.Type),
Handler: handler, Handler: handler,
client: client, client: client,
prepRevocation: make(chan error),
} }
session.Handler.StatusUpdate(session.Action, irma.StatusCommunicating) session.Handler.StatusUpdate(session.Action, irma.StatusCommunicating)
...@@ -304,6 +307,11 @@ func (session *session) processSessionInfo() { ...@@ -304,6 +307,11 @@ func (session *session) processSessionInfo() {
return 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 // Ask for permission to execute the session
callback := PermissionHandler(func(proceed bool, choice *irma.DisclosureChoice) { callback := PermissionHandler(func(proceed bool, choice *irma.DisclosureChoice) {
session.choice = choice session.choice = choice
...@@ -343,6 +351,12 @@ func (session *session) doSession(proceed bool) { ...@@ -343,6 +351,12 @@ func (session *session) doSession(proceed bool) {
} }
session.Handler.StatusUpdate(session.Action, irma.StatusCommunicating) 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() { if !session.Distributed() {
message, err := session.getProof() message, err := session.getProof()
if err != nil { if err != nil {
...@@ -455,6 +469,13 @@ func (session *session) sendResponse(message interface{}) { ...@@ -455,6 +469,13 @@ func (session *session) sendResponse(message interface{}) {
} }
session.done = true session.done = true
session.Handler.Success(string(messageJson)) 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). // 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 { ...@@ -229,6 +229,12 @@ func (b *BaseRequest) GetNonce(*atum.Timestamp) *big.Int {
return b.Nonce 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 const revocationUpdateCount = 5
// CredentialTypes returns an array of all credential types occuring in this conjunction. // CredentialTypes returns an array of all credential types occuring in this conjunction.
......
...@@ -72,7 +72,7 @@ func (rdb *DB) EnableRevocation(sk *revocation.PrivateKey) error { ...@@ -72,7 +72,7 @@ func (rdb *DB) EnableRevocation(sk *revocation.PrivateKey) error {
if err = rdb.Add(msg, sk.Counter); err != nil { if err = rdb.Add(msg, sk.Counter); err != nil {
return err return err
} }
rdb.Current = acc rdb.Current = *acc
return nil 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