Commit 2bd5fd48 authored by Sietse Ringers's avatar Sietse Ringers

feat: replace RA URL in scheme with URLs to servers hosting only revocation records

The URL to the server to which issuance records and revocation commands are sent now no longer needs to be put in the scheme, and may thus be kept private to the issuer. Instead multiple URLs are now in the scheme, which are only required to host revocation records for irmaclients and requestors to update their revocation state.
parent 113979e7
......@@ -57,19 +57,19 @@ type Issuer struct {
// CredentialType is a description of a credential type, specifying (a.o.) its name, issuer, and attributes.
type CredentialType struct {
ID string `xml:"CredentialID"`
Name TranslatedString `xml:"Name"`
ShortName TranslatedString `xml:"ShortName"`
IssuerID string `xml:"IssuerID"`
SchemeManagerID string `xml:"SchemeManager"`
IsSingleton bool `xml:"ShouldBeSingleton"`
DisallowDelete bool `xml:"DisallowDelete"`
Description TranslatedString
AttributeTypes []*AttributeType `xml:"Attributes>Attribute" json:"-"`
RevocationServer string
XMLVersion int `xml:"version,attr"`
XMLName xml.Name `xml:"IssueSpecification"`
IssueURL TranslatedString `xml:"IssueURL"`
ID string `xml:"CredentialID"`
Name TranslatedString `xml:"Name"`
ShortName TranslatedString `xml:"ShortName"`
IssuerID string `xml:"IssuerID"`
SchemeManagerID string `xml:"SchemeManager"`
IsSingleton bool `xml:"ShouldBeSingleton"`
DisallowDelete bool `xml:"DisallowDelete"`
Description TranslatedString
AttributeTypes []*AttributeType `xml:"Attributes>Attribute" json:"-"`
RevocationServers []string `xml:"RevocationServers>RevocationServer"`
XMLVersion int `xml:"version,attr"`
XMLName xml.Name `xml:"IssueSpecification"`
IssueURL TranslatedString `xml:"IssueURL"`
Valid bool `xml:"-"`
}
......@@ -99,7 +99,7 @@ func (ad AttributeType) IsOptional() bool {
}
func (ct *CredentialType) SupportsRevocation() bool {
return ct.RevocationServer != ""
return len(ct.RevocationServers) > 0
}
// ContainsAttribute tests whether the specified attribute is contained in this
......
......@@ -85,7 +85,7 @@ func (session *session) issuanceHandleRevocation(
}
// ensure the client always gets an up to date nonrevocation witness
if _, ours := session.conf.RevocationSettings[id]; !ours {
if settings, ok := session.conf.RevocationSettings[id]; !ok || settings.Mode != irma.RevocationModeServer {
if err = session.conf.IrmaConfiguration.RevocationStorage.UpdateDB(id); err != nil {
return
}
......
......@@ -123,6 +123,11 @@ func StartIrmaServer(t *testing.T, updatedIrmaConf bool) {
Logger: logger,
DisableSchemesUpdate: true,
SchemesPath: filepath.Join(testdata, irmaconf),
RevocationSettings: map[irma.CredentialTypeIdentifier]*irma.RevocationSetting{
irma.NewCredentialTypeIdentifier("irma-demo.MijnOverheid.root"): {
ServerURL: "http://localhost:48683/",
},
},
})
require.NoError(t, err)
......
......@@ -6,13 +6,13 @@ import (
)
var revokeEnableCmd = &cobra.Command{
Use: "enable CREDENTIALTYPE",
Use: "enable CREDENTIALTYPE URL",
Short: "Enable revocation for a credential type",
Long: `Enable revocation for a given credential type.
Must be done (and can only be done) by the issuer of the specified credential type, if enable in the
scheme.`,
Args: cobra.ExactArgs(1),
Args: cobra.ExactArgs(2),
Run: func(cmd *cobra.Command, args []string) {
flags := cmd.Flags()
schemespath, _ := flags.GetString("schemes-path")
......@@ -20,6 +20,7 @@ scheme.`,
key, _ := flags.GetString("key")
name, _ := flags.GetString("name")
verbosity, _ := cmd.Flags().GetCount("verbose")
url := args[1]
request := &irma.RevocationRequest{
LDContext: irma.LDContextRevocationRequest,
......@@ -27,7 +28,7 @@ scheme.`,
Enable: true,
}
postRevocation(request, schemespath, authmethod, key, name, verbosity)
postRevocation(request, url, schemespath, authmethod, key, name, verbosity)
},
}
......
......@@ -9,9 +9,9 @@ import (
)
var revokeCmd = &cobra.Command{
Use: "revoke CREDENTIALTYPE KEY",
Use: "revoke CREDENTIALTYPE KEY URL",
Short: "Revoke a previously issued credential identified by a given key",
Args: cobra.ExactArgs(2),
Args: cobra.ExactArgs(3),
Run: func(cmd *cobra.Command, args []string) {
flags := cmd.Flags()
schemespath, _ := flags.GetString("schemes-path")
......@@ -19,6 +19,7 @@ var revokeCmd = &cobra.Command{
key, _ := flags.GetString("key")
name, _ := flags.GetString("name")
verbosity, _ := cmd.Flags().GetCount("verbose")
url := args[2]
request := &irma.RevocationRequest{
LDContext: irma.LDContextRevocationRequest,
......@@ -26,11 +27,11 @@ var revokeCmd = &cobra.Command{
Key: args[1],
}
postRevocation(request, schemespath, authmethod, key, name, verbosity)
postRevocation(request, url, schemespath, authmethod, key, name, verbosity)
},
}
func postRevocation(request *irma.RevocationRequest, schemespath, authmethod, key, name string, verbosity int) {
func postRevocation(request *irma.RevocationRequest, url, schemespath, authmethod, key, name string, verbosity int) {
logger.Level = server.Verbosity(verbosity)
irma.Logger = logger
......@@ -46,11 +47,11 @@ func postRevocation(request *irma.RevocationRequest, schemespath, authmethod, ke
if !known {
die("unknown credential type", nil)
}
if credtype.RevocationServer == "" {
if !credtype.SupportsRevocation() {
die("credential type does not support revocation", nil)
}
transport := irma.NewHTTPTransport(credtype.RevocationServer)
transport := irma.NewHTTPTransport(url)
switch authmethod {
case "none":
......
......@@ -5,6 +5,7 @@ import (
"time"
"github.com/go-errors/errors"
"github.com/hashicorp/go-multierror"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/postgres"
"github.com/privacybydesign/gabi/big"
......@@ -43,6 +44,7 @@ type (
Mode RevocationMode `json:"mode"`
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"`
// set to now whenever a new revocation record is received, or when the RA indicates
// there are no new records. Thus it specifies up to what time our nonrevocation
......@@ -375,20 +377,22 @@ func (rs *RevocationStorage) UpdateIfOld(typ CredentialTypeIdentifier) error {
// SaveIssuanceRecord either stores the issuance record locally, if we are the revocation server of
// the crecential type, or it signs and sends it to the remote revocation server.
func (rs *RevocationStorage) SaveIssuanceRecord(typ CredentialTypeIdentifier, rec *IssuanceRecord) error {
// TODO store locally if appropriate?
// Just store it if we are the revocation server for this credential type
if rs.getSettings(typ).Mode == RevocationModeServer {
settings := rs.getSettings(typ)
if settings.Mode == RevocationModeServer {
return rs.AddIssuanceRecord(rec)
}
// We have to send it, sign it first
if settings.ServerURL == "" {
return errors.New("cannot send issuance record: no server_url configured")
}
credtype := rs.conf.CredentialTypes[typ]
if credtype == nil {
return errors.New("unknown credential type")
}
if credtype.RevocationServer == "" {
return errors.New("credential type has no revocation server")
if !credtype.SupportsRevocation() {
return errors.New("cannot send issuance record: credential type does not support revocation")
}
sk, err := rs.Keys.PrivateKey(typ.IssuerIdentifier())
if err != nil {
......@@ -399,20 +403,27 @@ func (rs *RevocationStorage) SaveIssuanceRecord(typ CredentialTypeIdentifier, re
return err
}
return rs.client.PostIssuanceRecord(typ, sk.Counter, message)
return rs.client.PostIssuanceRecord(typ, sk.Counter, message, settings.ServerURL)
}
// Misscelaneous methods
func (rs *RevocationStorage) Load(debug bool, connstr string, settings map[CredentialTypeIdentifier]*RevocationSetting) error {
var t *CredentialTypeIdentifier
for typ, s := range settings {
switch s.Mode {
case RevocationModeServer, RevocationModeProxy:
case RevocationModeServer:
if s.ServerURL != "" {
return errors.New("server_url cannot be combined with server mode")
}
t = &typ
case RevocationModeProxy:
t = &typ
case RevocationModeRequestor: // noop
default:
return errors.Errorf("invalid revocation mode '%s' for %s (supported: %s, %s)",
s.Mode, typ, RevocationModeServer, RevocationModeProxy)
return errors.Errorf(`invalid revocation mode "%s" for %s (supported: "%s", "%s", "%s")`,
s.Mode, typ, RevocationModeRequestor, RevocationModeServer, RevocationModeProxy)
}
}
if t != nil && connstr == "" {
......@@ -465,6 +476,9 @@ func (rs *RevocationStorage) SetRevocationRecords(b *BaseRequest) error {
var err error
b.RevocationUpdates = make(map[CredentialTypeIdentifier][]*RevocationRecord, len(b.Revocation))
for _, credid := range b.Revocation {
if !rs.conf.CredentialTypes[credid].SupportsRevocation() {
return errors.Errorf("cannot request nonrevocation proof for %s: revocation not enabled in scheme")
}
if err = rs.UpdateIfOld(credid); err != nil {
updated := rs.getSettings(credid).updated
if !updated.IsZero() {
......@@ -507,8 +521,8 @@ func (RevocationClient) PostRevocationRecords(urls []string, records []*Revocati
}
}
func (client RevocationClient) PostIssuanceRecord(typ CredentialTypeIdentifier, counter uint, message signed.Message) error {
return NewHTTPTransport(client.Conf.CredentialTypes[typ].RevocationServer).Post(
func (client RevocationClient) PostIssuanceRecord(typ CredentialTypeIdentifier, counter uint, message signed.Message, url string) error {
return NewHTTPTransport(url).Post(
fmt.Sprintf("-/revocation/issuancerecord/%s/%d", typ, counter), nil, []byte(message),
)
}
......@@ -516,22 +530,35 @@ func (client RevocationClient) PostIssuanceRecord(typ CredentialTypeIdentifier,
// FetchRevocationRecords gets revocation update messages from the revocation server, of the specified index and greater.
func (client RevocationClient) FetchRevocationRecords(typ CredentialTypeIdentifier, index uint64) ([]*RevocationRecord, error) {
var records []*RevocationRecord
err := NewHTTPTransport(client.Conf.CredentialTypes[typ].RevocationServer).
Get(fmt.Sprintf("-/revocation/records/%s/%d", typ, index), &records)
if err != nil {
return nil, err
var err error
var errs multierror.Error
transport := NewHTTPTransport("")
for _, url := range client.Conf.CredentialTypes[typ].RevocationServers {
transport.Server = url
err = transport.Get(fmt.Sprintf("-/revocation/records/%s/%d", typ, index), &records)
if err == nil {
return records, nil
} else {
errs.Errors = append(errs.Errors, err)
}
}
return records, nil
return nil, errors.WrapPrefix(errs, "failed to download revocation records", 0)
}
func (client RevocationClient) FetchLatestRevocationRecords(typ CredentialTypeIdentifier, count uint64) ([]*RevocationRecord, error) {
var records []*RevocationRecord
err := NewHTTPTransport(client.Conf.CredentialTypes[typ].RevocationServer).
Get(fmt.Sprintf("-/revocation/latestrecords/%s/%d", typ, count), &records)
if err != nil {
return nil, err
var errs multierror.Error
transport := NewHTTPTransport("")
for _, url := range client.Conf.CredentialTypes[typ].RevocationServers {
transport.Server = url
err := transport.Get(fmt.Sprintf("-/revocation/latestrecords/%s/%d", typ, count), &records)
if err == nil {
return records, nil
} else {
errs.Errors = append(errs.Errors, err)
}
}
return records, nil
return nil, errors.WrapPrefix(errs, "failed to download latest revocation records", 0)
}
func (rs RevocationKeys) PrivateKey(issid IssuerIdentifier) (*revocation.PrivateKey, error) {
......
......@@ -69,15 +69,15 @@ func (m memRevStorage) get(typ CredentialTypeIdentifier) *memRevRecords {
}
func (m memRevStorage) Latest(typ CredentialTypeIdentifier, count uint64, r *[]*RevocationRecord) {
ours := m.get(typ)
ours.Lock()
defer ours.Unlock()
records := m.get(typ)
records.Lock()
defer records.Unlock()
c := count
if c > uint64(len(ours.r)) {
c = uint64(len(ours.r))
if c > uint64(len(records.r)) {
c = uint64(len(records.r))
}
for _, rec := range ours.r[:c] {
for _, rec := range records.r[:c] {
Logger.Trace("membdb: get ", rec.StartIndex)
*r = append(*r, rec)
}
......
......@@ -202,17 +202,19 @@ func (conf *Configuration) verifyPrivateKeys() error {
}
func (conf *Configuration) verifyRevocation() error {
for credid := range conf.RevocationSettings {
for credid, settings := range conf.RevocationSettings {
if _, known := conf.IrmaConfiguration.CredentialTypes[credid]; !known {
return LogError(errors.Errorf("unknown credential type %s in revocation settings", credid))
}
enabled, err := conf.IrmaConfiguration.RevocationStorage.RevocationEnabled(credid)
if err != nil {
return LogError(errors.Errorf("failed to check if revocation is enabled for %s", credid.String()))
}
if !enabled {
return LogError(errors.Errorf("revocation not enabled for %s", credid.String()))
if settings.Mode == irma.RevocationModeServer {
enabled, err := conf.IrmaConfiguration.RevocationStorage.RevocationEnabled(credid)
if err != nil {
return LogError(errors.Errorf("failed to check if revocation is enabled for %s", credid.String()))
}
if !enabled {
return LogError(errors.Errorf("revocation not enabled for %s", credid.String()))
}
}
}
......
......@@ -16,7 +16,9 @@
</Description>
<IssueURL><en></en><nl></nl></IssueURL>
<ShouldBeSingleton>true</ShouldBeSingleton>
<RevocationServer>http://localhost:48683/</RevocationServer>
<RevocationServers>
<RevocationServer>http://localhost:48683/</RevocationServer>
</RevocationServers>
<Attributes>
<Attribute id="BSN">
......
1eeaa044eb9bc9177986762311d169016dfbe782688f668ddf4cfd804b4beeba irma-demo/MijnOverheid/Issues/fullName/description.xml
61a1fc7f161e43f8fc5b0c6ac2997cfe6bc0da7d27009b9914a04dca79ec6718 irma-demo/MijnOverheid/Issues/fullName/logo.png
77d6ce26354d573d6da40c5a2eb3f380b8ea42a6af59b0c927e55d5fb17bf2a4 irma-demo/MijnOverheid/Issues/root/description.xml
e732e3ffc6735631ca83f258d92cfbe71103f726385e7fa174588c154b887f87 irma-demo/MijnOverheid/Issues/root/description.xml
61a1fc7f161e43f8fc5b0c6ac2997cfe6bc0da7d27009b9914a04dca79ec6718 irma-demo/MijnOverheid/Issues/root/logo.png
3571e30777cdf5b97acbb0820f1b69983d2dc2b6bae91c8fb67cfc79ef4e2543 irma-demo/MijnOverheid/PublicKeys/0.xml
db0fdbe65ee9519290b756198a0d10b47c2af417e37843a2e790f5cc0e82d282 irma-demo/MijnOverheid/PublicKeys/1.xml
......@@ -15,4 +15,4 @@ dbd465d9cdb1c64206443e425fb1f8950605aee35e77f1e8bcf6cca8c34b8b65 irma-demo/RU/Pu
a4f6cc35cace3e9dc9388b29a8756ea83e5884f799d75cadd4efa60e1a12d855 irma-demo/RU/description.xml
35697bb7ffb19518a0ac6739ac3eef6b0272cd322c4619b075328b88c06ac43d irma-demo/RU/logo.png
ab4e98001906a4ad118ddb82794ef24e9f356980e15753f59d4720747f684e5b irma-demo/description.xml
aacd94be69cdb24da6d3bd57aa746bc1b90221e0fe3ef36671beb15fc47e4513 irma-demo/timestamp
e96581a11ca883ef02e60b1ef4ef75715cdcf15e61874744be2b47a5db3107d0 irma-demo/timestamp
......@@ -16,7 +16,9 @@
</Description>
<IssueURL><en></en><nl></nl></IssueURL>
<ShouldBeSingleton>true</ShouldBeSingleton>
<RevocationServer>http://localhost:48683/</RevocationServer>
<RevocationServers>
<RevocationServer>http://localhost:48683/</RevocationServer>
</RevocationServers>
<Attributes>
<Attribute id="BSN">
......
1eeaa044eb9bc9177986762311d169016dfbe782688f668ddf4cfd804b4beeba irma-demo/MijnOverheid/Issues/fullName/description.xml
61a1fc7f161e43f8fc5b0c6ac2997cfe6bc0da7d27009b9914a04dca79ec6718 irma-demo/MijnOverheid/Issues/fullName/logo.png
77d6ce26354d573d6da40c5a2eb3f380b8ea42a6af59b0c927e55d5fb17bf2a4 irma-demo/MijnOverheid/Issues/root/description.xml
e732e3ffc6735631ca83f258d92cfbe71103f726385e7fa174588c154b887f87 irma-demo/MijnOverheid/Issues/root/description.xml
61a1fc7f161e43f8fc5b0c6ac2997cfe6bc0da7d27009b9914a04dca79ec6718 irma-demo/MijnOverheid/Issues/root/logo.png
3571e30777cdf5b97acbb0820f1b69983d2dc2b6bae91c8fb67cfc79ef4e2543 irma-demo/MijnOverheid/PublicKeys/0.xml
db0fdbe65ee9519290b756198a0d10b47c2af417e37843a2e790f5cc0e82d282 irma-demo/MijnOverheid/PublicKeys/1.xml
......@@ -15,4 +15,4 @@ dbd465d9cdb1c64206443e425fb1f8950605aee35e77f1e8bcf6cca8c34b8b65 irma-demo/RU/Pu
a4f6cc35cace3e9dc9388b29a8756ea83e5884f799d75cadd4efa60e1a12d855 irma-demo/RU/description.xml
35697bb7ffb19518a0ac6739ac3eef6b0272cd322c4619b075328b88c06ac43d irma-demo/RU/logo.png
ab4e98001906a4ad118ddb82794ef24e9f356980e15753f59d4720747f684e5b irma-demo/description.xml
2702ed3b26876ec34306d69746286519afc378126afffdc9f47a4a02eba3a1b3 irma-demo/timestamp
7a7320cd9cd1d28f58022000f9a01568a74c3cb048a25dd3b501590261a3cbad irma-demo/timestamp
......@@ -16,7 +16,9 @@
</Description>
<IssueURL><en></en><nl></nl></IssueURL>
<ShouldBeSingleton>true</ShouldBeSingleton>
<RevocationServer>http://localhost:48683/</RevocationServer>
<RevocationServers>
<RevocationServer>http://localhost:48683/</RevocationServer>
</RevocationServers>
<Attributes>
<Attribute id="BSN">
......
1eeaa044eb9bc9177986762311d169016dfbe782688f668ddf4cfd804b4beeba irma-demo/MijnOverheid/Issues/fullName/description.xml
61a1fc7f161e43f8fc5b0c6ac2997cfe6bc0da7d27009b9914a04dca79ec6718 irma-demo/MijnOverheid/Issues/fullName/logo.png
77d6ce26354d573d6da40c5a2eb3f380b8ea42a6af59b0c927e55d5fb17bf2a4 irma-demo/MijnOverheid/Issues/root/description.xml
e732e3ffc6735631ca83f258d92cfbe71103f726385e7fa174588c154b887f87 irma-demo/MijnOverheid/Issues/root/description.xml
61a1fc7f161e43f8fc5b0c6ac2997cfe6bc0da7d27009b9914a04dca79ec6718 irma-demo/MijnOverheid/Issues/root/logo.png
3571e30777cdf5b97acbb0820f1b69983d2dc2b6bae91c8fb67cfc79ef4e2543 irma-demo/MijnOverheid/PublicKeys/0.xml
db0fdbe65ee9519290b756198a0d10b47c2af417e37843a2e790f5cc0e82d282 irma-demo/MijnOverheid/PublicKeys/1.xml
......@@ -15,4 +15,4 @@ dbd465d9cdb1c64206443e425fb1f8950605aee35e77f1e8bcf6cca8c34b8b65 irma-demo/RU/Pu
a4f6cc35cace3e9dc9388b29a8756ea83e5884f799d75cadd4efa60e1a12d855 irma-demo/RU/description.xml
35697bb7ffb19518a0ac6739ac3eef6b0272cd322c4619b075328b88c06ac43d irma-demo/RU/logo.png
ab4e98001906a4ad118ddb82794ef24e9f356980e15753f59d4720747f684e5b irma-demo/description.xml
2702ed3b26876ec34306d69746286519afc378126afffdc9f47a4a02eba3a1b3 irma-demo/timestamp
efd82d389f6dc5830abc00dab02c684a023d1a3c0d81d6990392aa8da8832683 irma-demo/timestamp
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