Commit f37717b6 authored by Sietse Ringers's avatar Sietse Ringers

feat: switch to SQL revocation database for revocation server, and to simple...

feat: switch to SQL revocation database for revocation server, and to simple in-memory database for requestors

Requestors only need to be aware of the last x revocation records and none of the other record types.

Addionally:
- pass revocation configuration into RevocationStorage
- refactor irma.NewConfiguration functions to use options struct
- split RevocationStorage helper functions into separate structs
- add credentialtype to revocation record wrapper in irmago
parent e83d71d3
package irma
import (
"database/sql/driver"
"fmt"
"strings"
"github.com/go-errors/errors"
"github.com/jinzhu/gorm"
)
type metaObjectIdentifier string
......@@ -276,3 +280,23 @@ func (set *IrmaIdentifierSet) String() string {
func (set *IrmaIdentifierSet) Empty() bool {
return len(set.SchemeManagers) == 0 && len(set.Issuers) == 0 && len(set.CredentialTypes) == 0 && len(set.PublicKeys) == 0 && len(set.AttributeTypes) == 0
}
func (oi metaObjectIdentifier) Value() (driver.Value, error) {
return oi.String(), nil
}
func (oi *metaObjectIdentifier) Scan(src interface{}) error {
s, ok := src.(string)
if !ok {
return errors.New("cannot convert source: not a string")
}
*oi = metaObjectIdentifier(s)
return nil
}
func (metaObjectIdentifier) GormDataType(dialect gorm.Dialect) string {
if dialect.GetName() == "postgres" {
return "text"
}
return ""
}
......@@ -14,7 +14,6 @@ import (
"github.com/go-errors/errors"
"github.com/jasonlvhit/gocron"
"github.com/privacybydesign/gabi/revocation"
"github.com/privacybydesign/irmago"
"github.com/privacybydesign/irmago/server"
"github.com/sirupsen/logrus"
......@@ -50,8 +49,7 @@ func New(conf *server.Configuration) (*Server, error) {
if !credtype.SupportsRevocation() {
continue
}
if _, ours := conf.RevocationServers[credid]; ours {
// TODO rethink this condition
if s := conf.RevocationSettings[credid]; s != nil && s.Mode != irma.RevocationModeRequestor {
continue
}
if err := s.conf.IrmaConfiguration.RevocationStorage.UpdateDB(credid); err != nil {
......@@ -145,7 +143,7 @@ func (s *Server) Revoke(credid irma.CredentialTypeIdentifier, key string) error
}
func ParsePath(path string) (token, noun string, arg []string, err error) {
rev := regexp.MustCompile("-/revocation/(records|issuancerecord)/?(.*)$")
rev := regexp.MustCompile("-/revocation/(records|latestrecords|issuancerecord)/?(.*)$")
matches := rev.FindStringSubmatch(path)
if len(matches) == 3 {
args := strings.Split(matches[2], "/")
......@@ -372,34 +370,37 @@ func (s *Server) handleClientMessage(
func (s *Server) handleRevocationMessage(
noun, method string, args []string, headers map[string][]string, message []byte,
) (int, []byte) {
if noun == "records" && method == http.MethodGet {
if (noun == "records" || noun == "latestrecords") && method == http.MethodGet {
if len(args) != 2 {
return server.JsonResponse(nil, server.RemoteError(server.ErrorInvalidRequest, "GET records expects 2 url arguments"))
return server.JsonResponse(nil, server.RemoteError(server.ErrorInvalidRequest, "GET "+noun+" expects 2 url arguments"))
}
index, err := strconv.Atoi(args[1])
i, err := strconv.ParseUint(args[1], 10, 64)
if err != nil {
return server.JsonResponse(nil, server.RemoteError(server.ErrorMalformedInput, err.Error()))
}
cred := irma.NewCredentialTypeIdentifier(args[0])
return server.JsonResponse(s.handleGetRevocationRecords(cred, index))
if noun == "records" {
return server.JsonResponse(s.handleGetRevocationRecords(cred, i))
} else {
return server.JsonResponse(s.handleGetLatestRevocationRecords(cred, i))
}
}
if noun == "records" && method == http.MethodPost {
if len(args) != 1 {
return server.JsonResponse(nil, server.RemoteError(server.ErrorInvalidRequest, "POST records expects 1 url arguments"))
if len(args) != 0 {
return server.JsonResponse(nil, server.RemoteError(server.ErrorInvalidRequest, "POST records expects no url arguments"))
}
cred := irma.NewCredentialTypeIdentifier(args[0])
var records []*revocation.Record
var records []*irma.RevocationRecord
if err := json.Unmarshal(message, &records); err != nil {
return server.JsonResponse(nil, server.RemoteError(server.ErrorMalformedInput, err.Error()))
}
return server.JsonResponse(s.handlePostRevocationRecords(cred, records))
return server.JsonResponse(s.handlePostRevocationRecords(records))
}
if noun == "issuancerecord" && method == http.MethodPost {
if len(args) != 2 {
return server.JsonResponse(nil, server.RemoteError(server.ErrorInvalidRequest, "POST issuancercord expects 2 url arguments"))
}
cred := irma.NewCredentialTypeIdentifier(args[0])
counter, err := strconv.Atoi(args[1])
counter, err := strconv.ParseUint(args[1], 10, 64)
if err != nil {
return server.JsonResponse(nil, server.RemoteError(server.ErrorMalformedInput, err.Error()))
}
......
......@@ -4,7 +4,6 @@ import (
"time"
"github.com/privacybydesign/gabi"
"github.com/privacybydesign/gabi/revocation"
"github.com/privacybydesign/gabi/signed"
"github.com/privacybydesign/irmago"
"github.com/privacybydesign/irmago/server"
......@@ -37,7 +36,7 @@ func (session *session) handleGetRequest(min, max *irma.ProtocolVersion) (irma.S
// we include the latest revocation records for the client here, as opposed to when the session
// was started, so that the client always gets the very latest revocation records
var err error
if err = session.conf.IrmaConfiguration.RevocationStorage.SetRecords(session.request.Base()); err != nil {
if err = session.conf.IrmaConfiguration.RevocationStorage.SetRevocationRecords(session.request.Base()); err != nil {
return nil, session.fail(server.ErrorUnknown, err.Error()) // TODO error type
}
......@@ -215,30 +214,33 @@ func (session *session) handlePostCommitments(commitments *irma.IssueCommitmentM
return sigs, nil
}
func (s *Server) handlePostRevocationRecords(
cred irma.CredentialTypeIdentifier, records []*revocation.Record,
) (interface{}, *irma.RemoteError) {
db, err := s.conf.IrmaConfiguration.RevocationStorage.DB(cred)
if err != nil {
return nil, server.RemoteError(server.ErrorUnknown, err.Error()) // TODO error type
}
if err = db.AddRecords(records); err != nil {
func (s *Server) handlePostRevocationRecords(records []*irma.RevocationRecord) (interface{}, *irma.RemoteError) {
if err := s.conf.IrmaConfiguration.RevocationStorage.AddRevocationRecords(records); err != nil {
return nil, server.RemoteError(server.ErrorUnknown, err.Error()) // TODO error type
}
return nil, nil
}
func (s *Server) handleGetRevocationRecords(
cred irma.CredentialTypeIdentifier, index int,
) ([]*revocation.Record, *irma.RemoteError) {
if _, ok := s.conf.RevocationServers[cred]; !ok {
cred irma.CredentialTypeIdentifier, index uint64,
) ([]*irma.RevocationRecord, *irma.RemoteError) {
if _, ok := s.conf.RevocationSettings[cred]; !ok {
return nil, server.RemoteError(server.ErrorInvalidRequest, "not supported by this server")
}
db, err := s.conf.IrmaConfiguration.RevocationStorage.DB(cred)
records, err := s.conf.IrmaConfiguration.RevocationStorage.RevocationRecords(cred, index)
if err != nil {
return nil, server.RemoteError(server.ErrorUnknown, err.Error()) // TODO error type
}
records, err := db.RevocationRecords(index)
return records, nil
}
func (s *Server) handleGetLatestRevocationRecords(
cred irma.CredentialTypeIdentifier, count uint64,
) ([]*irma.RevocationRecord, *irma.RemoteError) {
if _, ok := s.conf.RevocationSettings[cred]; !ok {
return nil, server.RemoteError(server.ErrorInvalidRequest, "not supported by this server")
}
records, err := s.conf.IrmaConfiguration.RevocationStorage.LatestRevocationRecords(cred, count)
if err != nil {
return nil, server.RemoteError(server.ErrorUnknown, err.Error()) // TODO error type
}
......@@ -246,38 +248,28 @@ func (s *Server) handleGetRevocationRecords(
}
func (s *Server) handlePostIssuanceRecord(
cred irma.CredentialTypeIdentifier, counter int, message []byte,
cred irma.CredentialTypeIdentifier, counter uint64, message []byte,
) (string, *irma.RemoteError) {
if _, ours := s.conf.RevocationServers[cred]; !ours {
if settings := s.conf.RevocationSettings[cred]; settings == nil || settings.Mode != irma.RevocationModeServer {
return "", server.RemoteError(server.ErrorInvalidRequest, "not supported by this server")
}
// Grab the counter-th issuer public key, with which the message should be signed,
// and verify and unmarshal the issuance record
pk, err := s.conf.IrmaConfiguration.PublicKey(cred.IssuerIdentifier(), counter)
if err != nil {
return "", server.RemoteError(server.ErrorUnknown, err.Error())
}
if pk == nil {
return "", server.RemoteError(server.ErrorUnknownPublicKey, "")
}
revpk, err := pk.RevocationKey()
pk, err := s.conf.IrmaConfiguration.RevocationStorage.Keys.PublicKey(cred.IssuerIdentifier(), uint(counter))
if err != nil {
return "", server.RemoteError(server.ErrorUnknown, err.Error())
}
var rec irma.IssuanceRecord
if err := signed.UnmarshalVerify(revpk.ECDSA, message, &rec); err != nil {
if err := signed.UnmarshalVerify(pk.ECDSA, message, &rec); err != nil {
return "", server.RemoteError(server.ErrorUnauthorized, err.Error())
}
// Insert the record into the database
db, err := s.conf.IrmaConfiguration.RevocationStorage.DB(cred)
if err != nil {
return "", server.RemoteError(server.ErrorUnknown, err.Error())
if rec.CredType != cred {
return "", server.RemoteError(server.ErrorInvalidRequest, "issuance record of wrong credential type")
}
if err = db.AddIssuanceRecord(&rec); err != nil {
if err = s.conf.IrmaConfiguration.RevocationStorage.AddIssuanceRecord(&rec); err != nil {
return "", server.RemoteError(server.ErrorUnknown, err.Error())
}
return "OK", nil
}
......@@ -79,47 +79,52 @@ func (session *session) checkCache(message []byte, expectedStatus server.Status)
func (session *session) issuanceHandleRevocation(
cred *irma.CredentialRequest, attributes *irma.AttributeList, sk *gabi.PrivateKey,
) (witness *revocation.Witness, nonrevAttr *big.Int, err error) {
if !session.conf.IrmaConfiguration.CredentialTypes[cred.CredentialTypeID].SupportsRevocation() {
id := cred.CredentialTypeID
if !session.conf.IrmaConfiguration.CredentialTypes[id].SupportsRevocation() {
return
}
// ensure the client always gets an up to date nonrevocation witness
if _, ours := session.conf.RevocationServers[cred.CredentialTypeID]; !ours {
if err = session.conf.IrmaConfiguration.RevocationStorage.UpdateDB(cred.CredentialTypeID); err != nil {
if _, ours := session.conf.RevocationSettings[id]; !ours {
if err = session.conf.IrmaConfiguration.RevocationStorage.UpdateDB(id); err != nil {
return
}
}
db, err := session.conf.IrmaConfiguration.RevocationStorage.DB(cred.CredentialTypeID)
if err != nil || !db.Enabled() {
return
}
rs := session.conf.IrmaConfiguration.RevocationStorage
records, err := db.LatestRecords(1)
// Fetch latest revocation record, and then extract the current value of the accumulator
// from it to generate the witness from
records, err := rs.LatestRevocationRecords(id, 1)
if err != nil {
return
}
if witness, err = sk.RevocationGenerateWitness(&db.Current); err != nil {
r := records[len(records)-1]
pk, err := rs.Keys.PublicKey(id.IssuerIdentifier(), r.PublicKeyIndex)
if err != nil {
return nil, nil, err
}
msg, err := r.UnmarshalVerify(pk)
if err != nil {
return nil, nil, err
}
if witness, err = sk.RevocationGenerateWitness(&msg.Accumulator); err != nil {
return
}
witness.Record = records[len(records)-1]
witness.Nu = nil // don't send to irmaclient, it will reconstruct it from witness.Record
witness.Index = 0 // same
witness.Record = &r.Record // attach previously selected reocation record to the witness for the client
witness.Nu = nil // don't send to irmaclient, it will reconstruct it from witness.Record
witness.Index = 0 // same
nonrevAttr = witness.E
issrecord := &irma.IssuanceRecord{
CredType: id,
Key: cred.RevocationKey,
Attr: nonrevAttr,
Issued: time.Now().UnixNano(), // or (floored) cred issuance time?
ValidUntil: attributes.Expiry().UnixNano(),
}
err = session.conf.IrmaConfiguration.RevocationStorage.SendIssuanceRecord(cred.CredentialTypeID, issrecord)
if err != nil {
_ = server.LogWarning(errors.WrapPrefix(err, "Failed to send issuance record to revocation server", 0))
session.conf.Logger.Warn("Storing issuance record locally")
if err = db.AddIssuanceRecord(issrecord); err != nil {
return nil, nil, err
}
}
err = session.conf.IrmaConfiguration.RevocationStorage.SaveIssuanceRecord(id, issrecord)
return
}
......@@ -147,22 +152,14 @@ func (s *Server) validateIssuanceRequest(request *irma.IssuanceRequest) error {
if err := cred.Validate(s.conf.IrmaConfiguration); err != nil {
return err
}
if s.conf.IrmaConfiguration.CredentialTypes[cred.CredentialTypeID].SupportsRevocation() {
db, err := s.conf.IrmaConfiguration.RevocationStorage.DB(cred.CredentialTypeID)
enabled, err := s.conf.IrmaConfiguration.RevocationStorage.RevocationEnabled(cred.CredentialTypeID)
if err != nil {
return err
}
if !db.Enabled() {
if !enabled {
s.conf.Logger.WithFields(logrus.Fields{"cred": cred.CredentialTypeID}).Warn("revocation supported in scheme but not enabled")
} else {
if len(cred.RevocationKey) == 0 {
return errors.New("revocationKey field unset on revocable credential")
}
if exists, err := db.IssuanceRecordExists([]byte(cred.RevocationKey)); err != nil {
return err
} else if exists {
return errors.New("revocationKey already used")
}
}
}
......
......@@ -6,6 +6,7 @@ import (
"testing"
"time"
"github.com/jinzhu/gorm"
irma "github.com/privacybydesign/irmago"
"github.com/privacybydesign/irmago/internal/test"
"github.com/privacybydesign/irmago/server"
......@@ -56,29 +57,42 @@ func StopRequestorServer() {
func StartRevocationServer(t *testing.T) {
var err error
irma.Logger = logger
dbstr := "host=127.0.0.1 port=5432 user=testuser dbname=test password='testpassword' sslmode=disable"
irmaconf, err := irma.NewConfiguration(filepath.Join(testdata, "irma_configuration"), irma.ConfigurationOptions{
RevocationDB: dbstr,
})
require.NoError(t, err)
require.NoError(t, irmaconf.ParseFolder())
cred := irma.NewCredentialTypeIdentifier("irma-demo.MijnOverheid.root")
conf := &server.Configuration{
Logger: logger,
DisableSchemesUpdate: true,
SchemesPath: filepath.Join(testdata, "irma_configuration"),
RevocationPath: filepath.Join(testdata, "tmp", "issuer"), // todo rename this path to revocation?
RevocationServers: map[irma.CredentialTypeIdentifier]server.RevocationServer{
cred: {},
RevocationSettings: map[irma.CredentialTypeIdentifier]*irma.RevocationSetting{
cred: {Mode: irma.RevocationModeServer},
},
IrmaConfiguration: irmaconf,
RevocationDB: dbstr,
}
revocationServer, err = irmaserver.New(conf)
require.NoError(t, err)
sk, err := conf.PrivateKey(cred.IssuerIdentifier())
require.NoError(t, err)
require.NotNil(t, sk)
revsk, err := sk.RevocationKey()
require.NoError(t, err)
db, err := conf.IrmaConfiguration.RevocationStorage.DB(cred)
require.NoError(t, err)
err = db.EnableRevocation(revsk)
// Connect to database and clear records from previous test runs
g, err := gorm.Open("postgres", conf.RevocationDB)
require.NoError(t, err)
require.NoError(t, g.DropTableIfExists((*irma.RevocationRecord)(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.IssuanceRecord)(nil)).Error)
require.NoError(t, g.Close())
// Enable revocation for our credential type
require.NoError(t, irmaconf.RevocationStorage.EnableRevocation(cred))
// Start revocation server
revocationServer, err = irmaserver.New(conf)
require.NoError(t, err)
mux := http.NewServeMux()
mux.HandleFunc("/", revocationServer.HandlerFunc())
revHttpServer = &http.Server{Addr: ":48683", Handler: mux}
......@@ -105,7 +119,6 @@ func StartIrmaServer(t *testing.T, updatedIrmaConf bool) {
Logger: logger,
DisableSchemesUpdate: true,
SchemesPath: filepath.Join(testdata, irmaconf),
RevocationPath: filepath.Join(testdata, "tmp", "revocation"),
})
require.NoError(t, err)
......@@ -129,7 +142,6 @@ var IrmaServerConfiguration = &requestorserver.Configuration{
Logger: logger,
SchemesPath: filepath.Join(testdata, "irma_configuration"),
IssuerPrivateKeysPath: filepath.Join(testdata, "privatekeys"),
RevocationPath: filepath.Join(testdata, "tmp", "revocation"),
},
DisableRequestorAuthentication: true,
Port: 48682,
......@@ -141,7 +153,6 @@ var JwtServerConfiguration = &requestorserver.Configuration{
Logger: logger,
SchemesPath: filepath.Join(testdata, "irma_configuration"),
IssuerPrivateKeysPath: filepath.Join(testdata, "privatekeys"),
RevocationPath: filepath.Join(testdata, "tmp", "revocation"),
},
Port: 48682,
DisableRequestorAuthentication: false,
......
......@@ -77,7 +77,10 @@ func downloadSchemeManager(dest string, urls []string) error {
}
}
conf, err := irma.NewConfiguration(dest)
conf, err := irma.NewConfiguration(dest, irma.ConfigurationOptions{})
if err != nil {
return err
}
if len(urls) == 0 {
if err := conf.DownloadDefaultSchemes(); err != nil {
......
......@@ -49,7 +49,7 @@ func printMetadataAttr(metaint *big.Int, confpath string) error {
if err := fs.AssertPathExists(confpath); err != nil {
return errors.WrapPrefix(err, "Cannot read irma_configuration", 0)
}
conf, err := irma.NewConfigurationReadOnly(confpath)
conf, err := irma.NewConfiguration(confpath, irma.ConfigurationOptions{ReadOnly: true})
if err != nil {
return errors.WrapPrefix(err, "Failed to parse irma_configuration", 0)
}
......
......@@ -80,7 +80,7 @@ func configureRequest(cmd *cobra.Command) (irma.RequestorRequest, *irma.Configur
if err != nil {
return nil, nil, err
}
irmaconfig, err := irma.NewConfiguration(irmaconfigPath)
irmaconfig, err := irma.NewConfiguration(irmaconfigPath, irma.ConfigurationOptions{})
if err != nil {
return nil, nil, err
}
......
......@@ -39,7 +39,7 @@ func configureRevocation(cmd *cobra.Command, path, credtype string) (*revocation
}
// parse irma_configuration and lookup credential type
irmaconf, err := irma.NewConfiguration(filepath.Join(path, "irma_configuration"))
irmaconf, err := irma.NewConfiguration(filepath.Join(path, "irma_configuration"), irma.ConfigurationOptions{})
if err != nil {
die("failed to open irma_configuration", err)
}
......
......@@ -84,7 +84,6 @@ func setFlags(cmd *cobra.Command, production bool) error {
}
schemespath := irma.DefaultSchemesPath()
revocationpath := filepath.Join(irma.DefaultDataPath(), "revocation")
flags.StringP("config", "c", "", "path to configuration file")
flags.StringP("schemes-path", "s", schemespath, "path to irma_configuration")
......@@ -94,7 +93,7 @@ func setFlags(cmd *cobra.Command, production bool) error {
flags.String("static-path", "", "Host files under this path as static files (leave empty to disable)")
flags.String("static-prefix", "/", "Host static files under this URL prefix")
flags.StringP("url", "u", defaulturl, "external URL to server to which the IRMA client connects, \":port\" being replaced by --port value")
flags.String("revocation-path", revocationpath, "path where revocation databases are stored")
flags.String("revocation-db", "", "connection string for revocation database")
flags.Bool("sse", false, "Enable server sent for status updates (experimental)")
flags.IntP("port", "p", 8088, "port at which to listen")
......@@ -201,7 +200,7 @@ func configureServer(cmd *cobra.Command) error {
SchemesUpdateInterval: viper.GetInt("schemes-update"),
DisableSchemesUpdate: viper.GetInt("schemes-update") == 0,
IssuerPrivateKeysPath: viper.GetString("privkeys"),
RevocationPath: viper.GetString("revocation-path"),
RevocationDB: viper.GetString("revocation-db"),
URL: viper.GetString("url"),
DisableTLS: viper.GetBool("no-tls"),
Email: viper.GetString("email"),
......
......@@ -56,7 +56,7 @@ func updateSchemeManager(paths []string) error {
}
irmaconf, manager := filepath.Dir(path), filepath.Base(path)
conf, err := irma.NewConfiguration(irmaconf)
conf, err := irma.NewConfiguration(irmaconf, irma.ConfigurationOptions{})
if err != nil {
return err
}
......
......@@ -63,7 +63,7 @@ func RunVerify(path string, verbose bool) error {
}
func VerifyScheme(path string) error {
conf, err := irma.NewConfigurationReadOnly(filepath.Dir(path))
conf, err := irma.NewConfiguration(filepath.Dir(path), irma.ConfigurationOptions{ReadOnly: true})
if err != nil {
return err
}
......@@ -88,7 +88,7 @@ func VerifyScheme(path string) error {
}
func VerifyIrmaConfiguration(path string) error {
conf, err := irma.NewConfigurationReadOnly(path)
conf, err := irma.NewConfiguration(path, irma.ConfigurationOptions{ReadOnly: true})
if err != nil {
return err
}
......
......@@ -142,7 +142,10 @@ func New(
handler: handler,
}
client.Configuration, err = irma.NewConfigurationFromAssets(filepath.Join(storagePath, "irma_configuration"), irmaConfigurationPath)
client.Configuration, err = irma.NewConfiguration(
filepath.Join(storagePath, "irma_configuration"),
irma.ConfigurationOptions{Assets: irmaConfigurationPath},
)
if err != nil {
return nil, err
}
......@@ -154,10 +157,6 @@ func New(
if schemeMgrErr != nil && !isSchemeMgrErr {
return nil, schemeMgrErr
}
client.Configuration.RevocationPath = filepath.Join(storagePath, "revocation")
if err = fs.EnsureDirectoryExists(client.Configuration.RevocationPath); err != nil {
return nil, err
}
// Ensure storage path exists, and populate it with necessary files
client.storage = storage{storagePath: storagePath, Configuration: client.Configuration}
......
package irmaclient
import (
"errors"
"github.com/privacybydesign/gabi"
"github.com/privacybydesign/gabi/revocation"
"github.com/privacybydesign/irmago"
......@@ -51,33 +53,44 @@ func (cred *credential) NonrevPrepare(conf *irma.Configuration, request irma.Ses
return false, nil
}
// first try to update witness by applying the revocation update messages attached to the session request
keys := irma.RevocationKeys{Conf: conf}
revupdates := request.Base().RevocationUpdates[credtype]
updated, err := cred.NonrevApplyUpdates(revupdates, conf.RevocationStorage)
updated, err := cred.NonrevApplyUpdates(revupdates, keys)
if err != nil {
return updated, err
}
// TODO (in both branches): attach our newer updates to response
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)
// nonrevocation witness is still out of date after applying the updates from the request:
// we were too far behind. Update from revocation server.
revupdates, err = irma.RevocationClient{Conf: conf}.FetchRevocationRecords(credtype, cred.NonRevocationWitness.Index+1)
if err != nil {
return updated, err
}
return cred.NonrevApplyUpdates(revupdates, conf.RevocationStorage)
return cred.NonrevApplyUpdates(revupdates, keys)
}
// 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.
func (cred *credential) NonrevApplyUpdates(messages []*revocation.Record, rs *irma.RevocationStorage) (bool, error) {
func (cred *credential) NonrevApplyUpdates(messages []*irma.RevocationRecord, keys irma.RevocationKeys) (bool, error) {
oldindex := cred.NonRevocationWitness.Index
if err := cred.NonRevocationWitness.Update(rs.Keystore(cred.CredentialType().IssuerIdentifier()), messages); err != nil {
return false, err
var err error
var pk *revocation.PublicKey
for _, record := range messages {
if cred.CredentialType().IssuerIdentifier() != record.CredType.IssuerIdentifier() {
return false, errors.New("cannot apply revocation record of other credential type")
}
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.Index != oldindex, cred.NonrevPrepareCache()
return cred.NonRevocationWitness.Index != oldindex, nil
}
......@@ -35,6 +35,7 @@ import (
"github.com/privacybydesign/gabi/big"
"github.com/privacybydesign/gabi/signed"
"github.com/privacybydesign/irmago/internal/fs"
"github.com/sirupsen/logrus"
)
// Configuration keeps track of scheme managers, issuers, credential types and public keys,
......@@ -48,8 +49,7 @@ type Configuration struct {
RevocationStorage *RevocationStorage
// Path to the irma_configuration folder that this instance represents
Path string
RevocationPath string
Path string
// DisabledSchemeManagers keeps track of scheme managers that did not parse succesfully
// (i.e., invalid signature, parsing error), and the problem that occurred when parsing them
......@@ -114,36 +114,25 @@ func (sme SchemeManagerError) Error() string {
return fmt.Sprintf("Error parsing scheme manager %s: %s", sme.Manager.Name(), sme.Err.Error())
}
// NewConfiguration returns a new configuration. After this
// ParseFolder() should be called to parse the specified path.
func NewConfiguration(path string) (*Configuration, error) {
return newConfiguration(path, "")
}