revocation.go 4.75 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
package irma

import (
	"fmt"
	"path/filepath"
	"time"

	"github.com/go-errors/errors"
	"github.com/hashicorp/go-multierror"
	"github.com/privacybydesign/gabi/revocation"
11
	"github.com/privacybydesign/gabi/signed"
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
)

func (conf *Configuration) RevocationKeystore(issuerid IssuerIdentifier) revocation.Keystore {
	return &issuerKeystore{issid: issuerid, conf: conf}
}

// issuerKeystore implements revocation.Keystore.
type issuerKeystore struct {
	issid IssuerIdentifier
	conf  *Configuration
}

var _ revocation.Keystore = (*issuerKeystore)(nil)

func (ks *issuerKeystore) PublicKey(counter uint) (*revocation.PublicKey, error) {
	pk, err := ks.conf.PublicKey(ks.issid, int(counter))
	if err != nil {
		return nil, err
	}
	if pk == nil {
		return nil, errors.Errorf("public key %d of issuer %s not found", counter, ks.issid)
	}
	if !pk.RevocationSupported() {
		return nil, errors.Errorf("public key %d of issuer %s does not support revocation", counter, ks.issid)
	}
	rpk, err := pk.RevocationKey()
	if err != nil {
		return nil, err
	}
	return rpk, nil
}

func (conf *Configuration) RevocationGetUpdates(credid CredentialTypeIdentifier, index uint64) ([]*revocation.Record, error) {
	var records []*revocation.Record
	err := NewHTTPTransport(conf.CredentialTypes[credid].RevocationServer).
		Get(fmt.Sprintf("-/revocation/records/%s/%d", credid, index), &records)
	if err != nil {
		return nil, err
	}
	return records, nil
}

func (conf *Configuration) RevocationUpdateAll() error {
	var err error
	for credid := range conf.revDBs {
		if err = conf.RevocationUpdateDB(credid); err != nil {
			return err
		}
	}
	return nil
}

func (conf *Configuration) RevocationSetRecords(b *BaseRequest) error {
	if len(b.Revocation) == 0 {
		return nil
	}
	b.RevocationUpdates = make(map[CredentialTypeIdentifier][]*revocation.Record, len(b.Revocation))
	for _, credid := range b.Revocation {
		db, err := conf.RevocationDB(credid)
		if err != nil {
			return err
		}
		if err = conf.revocationUpdateDelayed(credid, db); err != nil {
			return err
		}
		b.RevocationUpdates[credid], err = db.LatestRecords(revocationUpdateCount)
		if err != nil {
			return err
		}
	}
	return nil
}

func (conf *Configuration) RevocationUpdateDB(credid CredentialTypeIdentifier) error {
	db, err := conf.RevocationDB(credid)
	if err != nil {
		return err
	}
	var index uint64
	if db.Enabled() {
		index = db.Current.Index + 1
	}
	records, err := conf.RevocationGetUpdates(credid, index)
	if err != nil {
		return err
	}
	return db.AddRecords(records)
}

func (conf *Configuration) RevocationDB(credid CredentialTypeIdentifier) (*revocation.DB, error) {
	if _, known := conf.CredentialTypes[credid]; !known {
		return nil, errors.New("unknown credential type")
	}
	if conf.revDBs == nil {
		conf.revDBs = make(map[CredentialTypeIdentifier]*revocation.DB)
	}
	if conf.revDBs[credid] == nil {
		var err error
		db, err := revocation.LoadDB(
			filepath.Join(conf.RevocationPath, credid.String()),
			conf.RevocationKeystore(credid.IssuerIdentifier()),
		)
		if err != nil {
			return nil, err
		}
		conf.revDBs[credid] = db
	}
	return conf.revDBs[credid], nil
}

func (conf *Configuration) revocationUpdateDelayed(credid CredentialTypeIdentifier, db *revocation.DB) error {
	if db.Updated.Before(time.Now().Add(-5 * time.Minute)) {
		if err := conf.RevocationUpdateDB(credid); err != nil {
			return err
		}
	}
	return nil
}

131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
func (conf *Configuration) SendRevocationIssuanceRecord(
	cred CredentialTypeIdentifier, rec *revocation.IssuanceRecord,
) error {
	credtype := conf.CredentialTypes[cred]
	if credtype == nil {
		return errors.New("unknown credential type")
	}
	if credtype.RevocationServer == "" {
		return errors.New("credential type has no revocation server")
	}
	sk, err := conf.PrivateKey(cred.IssuerIdentifier())
	if err != nil {
		return err
	}
	if sk == nil {
		return errors.New("private key not found")
	}
	revsk, err := sk.RevocationKey()
	if err != nil {
		return err
	}

	message, err := signed.MarshalSign(revsk.ECDSA, rec)
	if err != nil {
		return err
	}
	return NewHTTPTransport(credtype.RevocationServer).Post(
		fmt.Sprintf("-/revocation/issuancerecord/%s/%d", cred, sk.Counter), nil, []byte(message),
	)
}

162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
func (conf *Configuration) Revoke(credid CredentialTypeIdentifier, key string) error {
	sk, err := conf.PrivateKey(credid.IssuerIdentifier())
	if err != nil {
		return err
	}
	if sk == nil {
		return errors.New("private key not found")
	}
	rsk, err := sk.RevocationKey()
	if err != nil {
		return err
	}

	db, err := conf.RevocationDB(credid)
	if err != nil {
		return err
	}
	return db.Revoke(rsk, []byte(key))
}

func (conf *Configuration) Close() error {
	merr := &multierror.Error{}
	var err error
	for _, db := range conf.revDBs {
		if err = db.Close(); err != nil {
			merr = multierror.Append(merr, err)
		}
	}
	conf.revDBs = nil
	return merr.ErrorOrNil()
}