session_test.go 17 KB
Newer Older
1
package sessiontest
Sietse Ringers's avatar
Sietse Ringers committed
2
3
4

import (
	"encoding/json"
5
6
	"io/ioutil"
	"path/filepath"
Sietse Ringers's avatar
Sietse Ringers committed
7
	"testing"
8
	"time"
Sietse Ringers's avatar
Sietse Ringers committed
9

10
	"github.com/dgrijalva/jwt-go"
11
	"github.com/privacybydesign/irmago"
12
	"github.com/privacybydesign/irmago/internal/fs"
13
	"github.com/privacybydesign/irmago/internal/test"
14
	"github.com/privacybydesign/irmago/irmaclient"
Sietse Ringers's avatar
Sietse Ringers committed
15
16
17
	"github.com/stretchr/testify/require"
)

18
19
20
21
func init() {
	irma.ForceHttps = false
}

Sietse Ringers's avatar
Sietse Ringers committed
22
23
24
func getDisclosureRequest(id irma.AttributeTypeIdentifier) *irma.DisclosureRequest {
	return &irma.DisclosureRequest{
		BaseRequest: irma.BaseRequest{Type: irma.ActionDisclosing},
25
		Content: irma.AttributeDisjunctionList([]*irma.AttributeDisjunction{{
Sietse Ringers's avatar
Sietse Ringers committed
26
			Label:      "foo",
27
			Attributes: []irma.AttributeTypeIdentifier{id},
Sietse Ringers's avatar
Sietse Ringers committed
28
		}}),
Sietse Ringers's avatar
Sietse Ringers committed
29
	}
Sietse Ringers's avatar
Sietse Ringers committed
30
31
}

Sietse Ringers's avatar
Sietse Ringers committed
32
33
func getSigningRequest(id irma.AttributeTypeIdentifier) *irma.SignatureRequest {
	return &irma.SignatureRequest{
34
		Message: "test",
35
		DisclosureRequest: irma.DisclosureRequest{
Sietse Ringers's avatar
Sietse Ringers committed
36
			BaseRequest: irma.BaseRequest{Type: irma.ActionSigning},
37
			Content: irma.AttributeDisjunctionList([]*irma.AttributeDisjunction{{
Sietse Ringers's avatar
Sietse Ringers committed
38
				Label:      "foo",
39
				Attributes: []irma.AttributeTypeIdentifier{id},
Sietse Ringers's avatar
Sietse Ringers committed
40
41
			}}),
		},
Sietse Ringers's avatar
Sietse Ringers committed
42
	}
Sietse Ringers's avatar
Sietse Ringers committed
43
44
}

45
func getIssuanceRequest(defaultValidity bool) *irma.IssuanceRequest {
46
	temp := irma.Timestamp(irma.FloorToEpochBoundary(time.Now().AddDate(1, 0, 0)))
47
	var expiry *irma.Timestamp
48

49
50
51
52
	if !defaultValidity {
		expiry = &temp
	}

53
	return &irma.IssuanceRequest{
Sietse Ringers's avatar
Sietse Ringers committed
54
		BaseRequest: irma.BaseRequest{Type: irma.ActionIssuing},
55
		Credentials: []*irma.CredentialRequest{
Sietse Ringers's avatar
Sietse Ringers committed
56
			{
57
				Validity:         expiry,
58
				CredentialTypeID: irma.NewCredentialTypeIdentifier("irma-demo.RU.studentCard"),
Sietse Ringers's avatar
Sietse Ringers committed
59
60
				Attributes: map[string]string{
					"university":        "Radboud",
61
					"studentCardNumber": "31415927",
Sietse Ringers's avatar
Sietse Ringers committed
62
63
64
65
					"studentID":         "s1234567",
					"level":             "42",
				},
			}, {
66
				Validity:         expiry,
67
				CredentialTypeID: irma.NewCredentialTypeIdentifier("irma-demo.MijnOverheid.root"),
Sietse Ringers's avatar
Sietse Ringers committed
68
69
70
71
72
				Attributes: map[string]string{
					"BSN": "299792458",
				},
			},
		},
73
74
75
	}
}

76
77
func getNameIssuanceRequest() *irma.IssuanceRequest {
	expiry := irma.Timestamp(irma.NewMetadataAttribute(0).Expiry())
78
79

	req := &irma.IssuanceRequest{
Sietse Ringers's avatar
Sietse Ringers committed
80
		BaseRequest: irma.BaseRequest{Type: irma.ActionIssuing},
81
82
83
		Credentials: []*irma.CredentialRequest{
			{
				Validity:         &expiry,
84
				CredentialTypeID: irma.NewCredentialTypeIdentifier("irma-demo.MijnOverheid.fullName"),
85
86
87
88
89
90
91
92
93
94
95
96
				Attributes: map[string]string{
					"firstnames": "Johan Pieter",
					"firstname":  "Johan",
					"familyname": "Stuivezand",
				},
			},
		},
	}

	return req
}

Sietse Ringers's avatar
Sietse Ringers committed
97
98
99
100
func getSpecialIssuanceRequest(defaultValidity bool, attribute string) *irma.IssuanceRequest {
	request := getIssuanceRequest(defaultValidity)
	request.Credentials[0].Attributes["studentCardNumber"] = attribute
	return request
101
102
}

Sietse Ringers's avatar
Sietse Ringers committed
103
104
105
func getCombinedIssuanceRequest(id irma.AttributeTypeIdentifier) *irma.IssuanceRequest {
	request := getIssuanceRequest(false)
	request.Disclose = irma.AttributeDisjunctionList{
106
107
		&irma.AttributeDisjunction{Label: "foo", Attributes: []irma.AttributeTypeIdentifier{id}},
	}
Sietse Ringers's avatar
Sietse Ringers committed
108
	return request
Sietse Ringers's avatar
Sietse Ringers committed
109
110
}

111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
var TestType = "irmaserver"

func startSession(t *testing.T, request irma.SessionRequest, sessiontype string) (*irma.Qr, string) {
	var qr irma.Qr
	var err error
	var token string

	switch TestType {
	case "apiserver":
		url := "http://localhost:8088/irma_api_server/api/v2/" + sessiontype
		err = irma.NewHTTPTransport(url).Post("", &qr, getJwt(t, request, sessiontype, false))
		token = qr.URL
		qr.URL = url + "/" + qr.URL
	case "irmaserver-jwt":
		url := "http://localhost:48682"
		err = irma.NewHTTPTransport(url).Post("create", &qr, getJwt(t, request, sessiontype, true))
		token = qr.URL
		qr.URL = url + "/irma/" + qr.URL
	case "irmaserver":
		url := "http://localhost:48682"
		err = irma.NewHTTPTransport(url).Post("create", &qr, request)
		token = qr.URL
		qr.URL = url + "/irma/" + qr.URL
	default:
		t.Fatal("Invalid TestType")
	}

	require.NoError(t, err)
	return &qr, token
}

func getJwt(t *testing.T, request irma.SessionRequest, sessiontype string, signed bool) string {
143
	var jwtcontents irma.RequestorJwt
144
145
	var kid string
	switch sessiontype {
Sietse Ringers's avatar
Sietse Ringers committed
146
	case "issue":
147
		kid = "testip"
Sietse Ringers's avatar
Sietse Ringers committed
148
149
		jwtcontents = irma.NewIdentityProviderJwt("testip", request.(*irma.IssuanceRequest))
	case "verification":
150
		kid = "testsp"
Sietse Ringers's avatar
Sietse Ringers committed
151
152
		jwtcontents = irma.NewServiceProviderJwt("testsp", request.(*irma.DisclosureRequest))
	case "signature":
153
		kid = "testsigclient"
Sietse Ringers's avatar
Sietse Ringers committed
154
		jwtcontents = irma.NewSignatureRequestorJwt("testsigclient", request.(*irma.SignatureRequest))
155
156
	}

157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
	var j string
	var err error
	if signed {
		skbts, err := ioutil.ReadFile(filepath.Join(test.FindTestdataFolder(t), "jwtkeys", "requestor1-sk.pem"))
		require.NoError(t, err)
		sk, err := jwt.ParseRSAPrivateKeyFromPEM(skbts)
		require.NoError(t, err)
		tok := jwt.NewWithClaims(jwt.SigningMethodRS256, jwtcontents)
		tok.Header["kid"] = "requestor1"
		j, err = tok.SignedString(sk)
	} else {
		tok := jwt.NewWithClaims(jwt.SigningMethodNone, jwtcontents)
		tok.Header["kid"] = kid
		j, err = tok.SignedString(jwt.UnsafeAllowNoneSignatureType)
	}
172
173
	require.NoError(t, err)

174
	return j
Sietse Ringers's avatar
Sietse Ringers committed
175
176
}

177
func sessionHelper(t *testing.T, request irma.SessionRequest, sessiontype string, client *irmaclient.Client) {
Sietse Ringers's avatar
Sietse Ringers committed
178
179
180
181
182
	if client == nil {
		client = parseStorage(t)
		defer test.ClearTestStorage(t)
	}

183
	qr, _ := startSession(t, request, sessiontype)
184
185
186

	c := make(chan *SessionResult)
	h := TestHandler{t, c, client}
187
	qrjson, err := json.Marshal(qr)
188
	require.NoError(t, err)
189
	client.NewSession(string(qrjson), h)
190
191
192
193
194
195

	if result := <-c; result != nil {
		require.NoError(t, result.Err)
	}
}

196
func keyshareSessions(t *testing.T, client *irmaclient.Client) {
197
198
	id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")
	expiry := irma.Timestamp(irma.NewMetadataAttribute(0).Expiry())
Sietse Ringers's avatar
Sietse Ringers committed
199
200
	issuanceRequest := getCombinedIssuanceRequest(id)
	issuanceRequest.Credentials = append(issuanceRequest.Credentials,
201
202
		&irma.CredentialRequest{
			Validity:         &expiry,
203
			CredentialTypeID: irma.NewCredentialTypeIdentifier("test.test.mijnirma"),
204
205
206
			Attributes:       map[string]string{"email": "testusername"},
		},
	)
Sietse Ringers's avatar
Sietse Ringers committed
207
	sessionHelper(t, issuanceRequest, "issue", client)
208

Sietse Ringers's avatar
Sietse Ringers committed
209
210
	disclosureRequest := getDisclosureRequest(id)
	disclosureRequest.Content = append(disclosureRequest.Content,
211
212
213
214
215
		&irma.AttributeDisjunction{
			Label:      "foo",
			Attributes: []irma.AttributeTypeIdentifier{irma.NewAttributeTypeIdentifier("test.test.mijnirma.email")},
		},
	)
Sietse Ringers's avatar
Sietse Ringers committed
216
	sessionHelper(t, disclosureRequest, "verification", client)
217

Sietse Ringers's avatar
Sietse Ringers committed
218
219
	sigRequest := getSigningRequest(id)
	sigRequest.Content = append(sigRequest.Content,
220
221
222
223
224
		&irma.AttributeDisjunction{
			Label:      "foo",
			Attributes: []irma.AttributeTypeIdentifier{irma.NewAttributeTypeIdentifier("test.test.mijnirma.email")},
		},
	)
Sietse Ringers's avatar
Sietse Ringers committed
225
	sessionHelper(t, sigRequest, "signature", client)
226
227
}

Sietse Ringers's avatar
Sietse Ringers committed
228
func TestSigningSession(t *testing.T) {
229
	id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")
Sietse Ringers's avatar
Sietse Ringers committed
230
231
	request := getSigningRequest(id)
	sessionHelper(t, request, "signature", nil)
Sietse Ringers's avatar
Sietse Ringers committed
232
233
234
}

func TestDisclosureSession(t *testing.T) {
235
	id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")
Sietse Ringers's avatar
Sietse Ringers committed
236
237
	request := getDisclosureRequest(id)
	sessionHelper(t, request, "verification", nil)
Sietse Ringers's avatar
Sietse Ringers committed
238
239
}

240
241
func TestNoAttributeDisclosureSession(t *testing.T) {
	id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard")
Sietse Ringers's avatar
Sietse Ringers committed
242
243
	request := getDisclosureRequest(id)
	sessionHelper(t, request, "verification", nil)
244
245
}

Sietse Ringers's avatar
Sietse Ringers committed
246
func TestIssuanceSession(t *testing.T) {
247
	id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")
Sietse Ringers's avatar
Sietse Ringers committed
248
249
	request := getCombinedIssuanceRequest(id)
	sessionHelper(t, request, "issue", nil)
Sietse Ringers's avatar
Sietse Ringers committed
250
251
}

252
253
func TestDefaultCredentialValidity(t *testing.T) {
	client := parseStorage(t)
Sietse Ringers's avatar
Sietse Ringers committed
254
255
	request := getIssuanceRequest(true)
	sessionHelper(t, request, "issue", client)
256
257
}

258
func TestIssuanceOptionalEmptyAttributes(t *testing.T) {
259
	req := getNameIssuanceRequest()
Sietse Ringers's avatar
Sietse Ringers committed
260
	sessionHelper(t, req, "issue", nil)
261
262
263
264
265
}

func TestIssuanceOptionalZeroLengthAttributes(t *testing.T) {
	req := getNameIssuanceRequest()
	req.Credentials[0].Attributes["prefix"] = ""
Sietse Ringers's avatar
Sietse Ringers committed
266
	sessionHelper(t, req, "issue", nil)
267
268
269
}

func TestIssuanceOptionalSetAttributes(t *testing.T) {
270
271
	req := getNameIssuanceRequest()
	req.Credentials[0].Attributes["prefix"] = "van"
Sietse Ringers's avatar
Sietse Ringers committed
272
	sessionHelper(t, req, "issue", nil)
273
274
}

275
276
func TestLargeAttribute(t *testing.T) {
	client := parseStorage(t)
277
278
	defer test.ClearTestStorage(t)

279
280
	require.NoError(t, client.RemoveAllCredentials())

Sietse Ringers's avatar
Sietse Ringers committed
281
282
	issuanceRequest := getSpecialIssuanceRequest(false, "1234567890123456789012345678901234567890") // 40 chars
	sessionHelper(t, issuanceRequest, "issue", client)
283

Sietse Ringers's avatar
Sietse Ringers committed
284
285
	disclosureRequest := getDisclosureRequest(irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.university"))
	sessionHelper(t, disclosureRequest, "verification", client)
286
287
}

288
289
func TestIssuanceSingletonCredential(t *testing.T) {
	client := parseStorage(t)
290
291
	defer test.ClearTestStorage(t)

Sietse Ringers's avatar
Sietse Ringers committed
292
	request := getIssuanceRequest(true)
293
294
	credid := irma.NewCredentialTypeIdentifier("irma-demo.MijnOverheid.root")

295
	require.Nil(t, client.Attributes(credid, 0))
296

Sietse Ringers's avatar
Sietse Ringers committed
297
	sessionHelper(t, request, "issue", client)
298
299
	require.NotNil(t, client.Attributes(credid, 0))
	require.Nil(t, client.Attributes(credid, 1))
300

Sietse Ringers's avatar
Sietse Ringers committed
301
	sessionHelper(t, request, "issue", client)
302
303
	require.NotNil(t, client.Attributes(credid, 0))
	require.Nil(t, client.Attributes(credid, 1))
304
305
}

306
307
308
309
310
311
312
313
/* There is an annoying difference between how Java and Go convert big integers to and from
byte arrays: in Java the sign of the integer is taken into account, but not in Go. This means
that in Java, when converting a bigint to or from a byte array, the most significant bit
indicates the sign of the integer. In Go this is not the case. This resulted in invalid
signatures being issued in the issuance protocol in two distinct ways, of which we test here
that they have been fixed. */
func TestAttributeByteEncoding(t *testing.T) {
	client := parseStorage(t)
314
	defer test.ClearTestStorage(t)
315
316
317
318
319
320
321
	require.NoError(t, client.RemoveAllCredentials())

	/* After bitshifting the presence bit into the large attribute below, the most significant
	bit is 1. In the bigint->[]byte conversion that happens before hashing this attribute, in
	Java this results in an extra 0 byte being prepended in order to have a 0 instead as most
	significant (sign) bit. We test that the Java implementation correctly removes the extraneous
	0 byte. */
Sietse Ringers's avatar
Sietse Ringers committed
322
323
	request := getSpecialIssuanceRequest(false, "a23456789012345678901234567890")
	sessionHelper(t, request, "issue", client)
324
325
326
327
328
329
330

	/* After converting the attribute below to bytes (using UTF8, on which Java and Go do agree),
	the most significant bit of the byte version of this attribute is 1. In the []byte->bigint
	conversion that happens at that point in the Java implementation (bitshifting is done
	afterwards), this results in a negative number in Java and a positive number in Go. We test
	here that the Java correctly prepends a 0 byte just before this conversion in order to get
	the same positive bigint. */
Sietse Ringers's avatar
Sietse Ringers committed
331
332
	request = getSpecialIssuanceRequest(false, "é")
	sessionHelper(t, request, "issue", client)
333
334
}

335
// Use the existing keyshare enrollment and credentials
Sietse Ringers's avatar
Sietse Ringers committed
336
337
338
339
// in a keyshare session of each session type.
// Use keyshareuser.sql to enroll the user at the keyshare server.
func TestKeyshareSessions(t *testing.T) {
	client := parseStorage(t)
340
	defer test.ClearTestStorage(t)
Sietse Ringers's avatar
Sietse Ringers committed
341

342
	keyshareSessions(t, client)
Sietse Ringers's avatar
Sietse Ringers committed
343
}
344
345
346

func TestDisclosureNewAttributeUpdateSchemeManager(t *testing.T) {
	client := parseStorage(t)
347
	defer test.ClearTestStorage(t)
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371

	schemeid := irma.NewSchemeManagerIdentifier("irma-demo")
	credid := irma.NewCredentialTypeIdentifier("irma-demo.RU.studentCard")
	attrid := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.newAttribute")
	require.False(t, client.Configuration.CredentialTypes[credid].ContainsAttribute(attrid))

	client.Configuration.SchemeManagers[schemeid].URL = "http://localhost:48681/irma_configuration_updated/irma-demo"
	disclosureRequest := irma.DisclosureRequest{
		Content: irma.AttributeDisjunctionList{
			&irma.AttributeDisjunction{
				Label: "foo",
				Attributes: []irma.AttributeTypeIdentifier{
					attrid,
				},
			},
		},
	}

	client.Configuration.Download(&disclosureRequest)
	require.True(t, client.Configuration.CredentialTypes[credid].ContainsAttribute(attrid))
}

func TestIssueNewAttributeUpdateSchemeManager(t *testing.T) {
	client := parseStorage(t)
372
373
	defer test.ClearTestStorage(t)

374
375
376
377
378
379
380
381
382
383
384
385
386
387
	schemeid := irma.NewSchemeManagerIdentifier("irma-demo")
	credid := irma.NewCredentialTypeIdentifier("irma-demo.RU.studentCard")
	attrid := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.newAttribute")
	require.False(t, client.Configuration.CredentialTypes[credid].ContainsAttribute(attrid))

	client.Configuration.SchemeManagers[schemeid].URL = "http://localhost:48681/irma_configuration_updated/irma-demo"
	issuanceRequest := getIssuanceRequest(true)
	issuanceRequest.Credentials[0].Attributes["newAttribute"] = "foobar"
	client.Configuration.Download(issuanceRequest)
	require.True(t, client.Configuration.CredentialTypes[credid].ContainsAttribute(attrid))
}

func TestIssueOptionalAttributeUpdateSchemeManager(t *testing.T) {
	client := parseStorage(t)
388
389
	defer test.ClearTestStorage(t)

390
391
392
393
394
395
396
397
398
399
400
401
	schemeid := irma.NewSchemeManagerIdentifier("irma-demo")
	credid := irma.NewCredentialTypeIdentifier("irma-demo.RU.studentCard")
	attrid := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.level")
	require.False(t, client.Configuration.CredentialTypes[credid].AttributeType(attrid).IsOptional())

	client.Configuration.SchemeManagers[schemeid].URL = "http://localhost:48681/irma_configuration_updated/irma-demo"
	issuanceRequest := getIssuanceRequest(true)
	delete(issuanceRequest.Credentials[0].Attributes, "level")
	client.Configuration.Download(issuanceRequest)
	require.True(t, client.Configuration.CredentialTypes[credid].AttributeType(attrid).IsOptional())
}

Sietse Ringers's avatar
Sietse Ringers committed
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
func TestIssueNewCredTypeUpdateSchemeManager(t *testing.T) {
	client := parseStorage(t)
	schemeid := irma.NewSchemeManagerIdentifier("irma-demo")
	credid := irma.NewCredentialTypeIdentifier("irma-demo.RU.studentCard")

	delete(client.Configuration.CredentialTypes, credid)
	require.NotContains(t, client.Configuration.CredentialTypes, credid)

	client.Configuration.SchemeManagers[schemeid].URL = "http://localhost:48681/irma_configuration_updated/irma-demo"
	request := getIssuanceRequest(true)
	client.Configuration.Download(request)

	require.Contains(t, client.Configuration.CredentialTypes, credid)

	test.ClearTestStorage(t)
}

func TestDisclosureNewCredTypeUpdateSchemeManager(t *testing.T) {
	client := parseStorage(t)
	schemeid := irma.NewSchemeManagerIdentifier("irma-demo")
	credid := irma.NewCredentialTypeIdentifier("irma-demo.RU.studentCard")
	attrid := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.level")

	delete(client.Configuration.CredentialTypes, credid)
	require.NotContains(t, client.Configuration.CredentialTypes, credid)

	client.Configuration.SchemeManagers[schemeid].URL = "http://localhost:48681/irma_configuration_updated/irma-demo"
	request := &irma.DisclosureRequest{
		Content: irma.AttributeDisjunctionList([]*irma.AttributeDisjunction{{
			Label:      "foo",
			Attributes: []irma.AttributeTypeIdentifier{attrid},
		}}),
	}
	client.Configuration.Download(request)

	require.Contains(t, client.Configuration.CredentialTypes, credid)

	test.ClearTestStorage(t)
}

442
443
444
445
446
// Test installing a new scheme manager from a qr, and do a(n issuance) session
// within this manager to test the autmatic downloading of credential definitions,
// issuers, and public keys.
func TestDownloadSchemeManager(t *testing.T) {
	client := parseStorage(t)
447
	defer test.ClearTestStorage(t)
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468

	// Remove irma-demo scheme manager as we need to test adding it
	irmademo := irma.NewSchemeManagerIdentifier("irma-demo")
	require.Contains(t, client.Configuration.SchemeManagers, irmademo)
	require.NoError(t, client.Configuration.RemoveSchemeManager(irmademo, true))
	require.NotContains(t, client.Configuration.SchemeManagers, irmademo)

	// Do an add-scheme-manager-session
	c := make(chan *SessionResult)
	qr, err := json.Marshal(&irma.SchemeManagerRequest{
		Type: irma.ActionSchemeManager,
		URL:  "http://localhost:48681/irma_configuration/irma-demo",
	})
	require.NoError(t, err)
	client.NewSession(string(qr), TestHandler{t, c, client})
	if result := <-c; result != nil {
		require.NoError(t, result.Err)
	}
	require.Contains(t, client.Configuration.SchemeManagers, irmademo)

	// Do a session to test downloading of cred types, issuers and keys
Sietse Ringers's avatar
Sietse Ringers committed
469
470
	request := getCombinedIssuanceRequest(irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID"))
	sessionHelper(t, request, "issue", client)
471
472
473
474
475

	require.Contains(t, client.Configuration.SchemeManagers, irmademo)
	require.Contains(t, client.Configuration.Issuers, irma.NewIssuerIdentifier("irma-demo.RU"))
	require.Contains(t, client.Configuration.CredentialTypes, irma.NewCredentialTypeIdentifier("irma-demo.RU.studentCard"))

476
	basepath := test.FindTestdataFolder(t) + "/storage/test/irma_configuration/irma-demo"
477
478
479
480
481
482
483
484
485
486
	exists, err := fs.PathExists(basepath + "/description.xml")
	require.NoError(t, err)
	require.True(t, exists)
	exists, err = fs.PathExists(basepath + "/RU/description.xml")
	require.NoError(t, err)
	require.True(t, exists)
	exists, err = fs.PathExists(basepath + "/RU/Issues/studentCard/description.xml")
	require.NoError(t, err)
	require.True(t, exists)
}