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

import (
	"encoding/base64"
	"encoding/json"
	"testing"
7
	"time"
Sietse Ringers's avatar
Sietse Ringers committed
8

9
	"github.com/privacybydesign/irmago"
10
	"github.com/privacybydesign/irmago/internal/fs"
11
	"github.com/privacybydesign/irmago/internal/test"
12
	"github.com/privacybydesign/irmago/irmaclient"
Sietse Ringers's avatar
Sietse Ringers committed
13
14
15
	"github.com/stretchr/testify/require"
)

16
17
18
19
func init() {
	irma.ForceHttps = false
}

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

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

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

47
48
49
50
	if !defaultValidity {
		expiry = &temp
	}

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

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

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

	return req
}

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

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

109
// startSession starts an IRMA session by posting the request,
Sietse Ringers's avatar
Sietse Ringers committed
110
// and retrieving the QR contents from the specified url.
111
func startSession(request interface{}, url string) (*irma.Qr, error) {
112
113
	server := irma.NewHTTPTransport(url)
	var response irma.Qr
Sietse Ringers's avatar
Sietse Ringers committed
114
115
116
117
118
119
120
	err := server.Post("", &response, request)
	if err != nil {
		return nil, err
	}
	return &response, nil
}

Sietse Ringers's avatar
Sietse Ringers committed
121
122
123
124
125
126
127
128
129
func getJwt(t *testing.T, request irma.SessionRequest, url string) string {
	var jwtcontents interface{}
	switch url {
	case "issue":
		jwtcontents = irma.NewIdentityProviderJwt("testip", request.(*irma.IssuanceRequest))
	case "verification":
		jwtcontents = irma.NewServiceProviderJwt("testsp", request.(*irma.DisclosureRequest))
	case "signature":
		jwtcontents = irma.NewSignatureRequestorJwt("testsigclient", request.(*irma.SignatureRequest))
130
131
132
133
134
135
136
	}

	headerbytes, err := json.Marshal(&map[string]string{"alg": "none", "typ": "JWT"})
	require.NoError(t, err)
	bodybytes, err := json.Marshal(jwtcontents)
	require.NoError(t, err)

Sietse Ringers's avatar
Sietse Ringers committed
137
138
139
140
141
142
143
144
145
	return base64.RawStdEncoding.EncodeToString(headerbytes) + "." + base64.RawStdEncoding.EncodeToString(bodybytes) + "."
}

func sessionHelper(t *testing.T, request irma.SessionRequest, url string, client *irmaclient.Client) {
	if client == nil {
		client = parseStorage(t)
		defer test.ClearTestStorage(t)
	}

146
147
148
149
150
151
152
153
154
155
156
157
158
159
	transport := irma.NewHTTPTransport("http://localhost:48682")
	var qr irma.Qr
	err := transport.Post("create", &qr, request)
	require.NoError(t, err)
	qr.URL = "http://localhost:48682/irma/" + qr.URL

	//jwt := getJwt(t, request, url)
	//url = "http://localhost:8088/irma_api_server/api/v2/" + url
	//qr, transportErr := startSession(jwt, url)
	//if transportErr != nil {
	//	fmt.Printf("+%v\n", transportErr)
	//}
	//require.NoError(t, transportErr)
	//qr.URL = url + "/" + qr.URL
160
161
162

	c := make(chan *SessionResult)
	h := TestHandler{t, c, client}
163
164
165
	j, err := json.Marshal(qr)
	require.NoError(t, err)
	client.NewSession(string(j), h)
166
167
168
169
170
171

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

172
func keyshareSessions(t *testing.T, client *irmaclient.Client) {
173
174
	id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")
	expiry := irma.Timestamp(irma.NewMetadataAttribute(0).Expiry())
Sietse Ringers's avatar
Sietse Ringers committed
175
176
	issuanceRequest := getCombinedIssuanceRequest(id)
	issuanceRequest.Credentials = append(issuanceRequest.Credentials,
177
178
		&irma.CredentialRequest{
			Validity:         &expiry,
179
			CredentialTypeID: irma.NewCredentialTypeIdentifier("test.test.mijnirma"),
180
181
182
			Attributes:       map[string]string{"email": "testusername"},
		},
	)
Sietse Ringers's avatar
Sietse Ringers committed
183
	sessionHelper(t, issuanceRequest, "issue", client)
184

Sietse Ringers's avatar
Sietse Ringers committed
185
186
	disclosureRequest := getDisclosureRequest(id)
	disclosureRequest.Content = append(disclosureRequest.Content,
187
188
189
190
191
		&irma.AttributeDisjunction{
			Label:      "foo",
			Attributes: []irma.AttributeTypeIdentifier{irma.NewAttributeTypeIdentifier("test.test.mijnirma.email")},
		},
	)
Sietse Ringers's avatar
Sietse Ringers committed
192
	sessionHelper(t, disclosureRequest, "verification", client)
193

Sietse Ringers's avatar
Sietse Ringers committed
194
195
	sigRequest := getSigningRequest(id)
	sigRequest.Content = append(sigRequest.Content,
196
197
198
199
200
		&irma.AttributeDisjunction{
			Label:      "foo",
			Attributes: []irma.AttributeTypeIdentifier{irma.NewAttributeTypeIdentifier("test.test.mijnirma.email")},
		},
	)
Sietse Ringers's avatar
Sietse Ringers committed
201
	sessionHelper(t, sigRequest, "signature", client)
202
203
}

Sietse Ringers's avatar
Sietse Ringers committed
204
func TestSigningSession(t *testing.T) {
205
	id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")
Sietse Ringers's avatar
Sietse Ringers committed
206
207
	request := getSigningRequest(id)
	sessionHelper(t, request, "signature", nil)
Sietse Ringers's avatar
Sietse Ringers committed
208
209
210
}

func TestDisclosureSession(t *testing.T) {
211
	id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")
Sietse Ringers's avatar
Sietse Ringers committed
212
213
	request := getDisclosureRequest(id)
	sessionHelper(t, request, "verification", nil)
Sietse Ringers's avatar
Sietse Ringers committed
214
215
}

216
217
func TestNoAttributeDisclosureSession(t *testing.T) {
	id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard")
Sietse Ringers's avatar
Sietse Ringers committed
218
219
	request := getDisclosureRequest(id)
	sessionHelper(t, request, "verification", nil)
220
221
}

Sietse Ringers's avatar
Sietse Ringers committed
222
func TestIssuanceSession(t *testing.T) {
223
	id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")
Sietse Ringers's avatar
Sietse Ringers committed
224
225
	request := getCombinedIssuanceRequest(id)
	sessionHelper(t, request, "issue", nil)
Sietse Ringers's avatar
Sietse Ringers committed
226
227
}

228
229
func TestDefaultCredentialValidity(t *testing.T) {
	client := parseStorage(t)
Sietse Ringers's avatar
Sietse Ringers committed
230
231
	request := getIssuanceRequest(true)
	sessionHelper(t, request, "issue", client)
232
233
}

234
func TestIssuanceOptionalEmptyAttributes(t *testing.T) {
235
	req := getNameIssuanceRequest()
Sietse Ringers's avatar
Sietse Ringers committed
236
	sessionHelper(t, req, "issue", nil)
237
238
239
240
241
}

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

func TestIssuanceOptionalSetAttributes(t *testing.T) {
246
247
	req := getNameIssuanceRequest()
	req.Credentials[0].Attributes["prefix"] = "van"
Sietse Ringers's avatar
Sietse Ringers committed
248
	sessionHelper(t, req, "issue", nil)
249
250
}

251
252
func TestLargeAttribute(t *testing.T) {
	client := parseStorage(t)
253
254
	defer test.ClearTestStorage(t)

255
256
	require.NoError(t, client.RemoveAllCredentials())

Sietse Ringers's avatar
Sietse Ringers committed
257
258
	issuanceRequest := getSpecialIssuanceRequest(false, "1234567890123456789012345678901234567890") // 40 chars
	sessionHelper(t, issuanceRequest, "issue", client)
259

Sietse Ringers's avatar
Sietse Ringers committed
260
261
	disclosureRequest := getDisclosureRequest(irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.university"))
	sessionHelper(t, disclosureRequest, "verification", client)
262
263
}

264
265
func TestIssuanceSingletonCredential(t *testing.T) {
	client := parseStorage(t)
266
267
	defer test.ClearTestStorage(t)

Sietse Ringers's avatar
Sietse Ringers committed
268
	request := getIssuanceRequest(true)
269
270
	credid := irma.NewCredentialTypeIdentifier("irma-demo.MijnOverheid.root")

271
	require.Nil(t, client.Attributes(credid, 0))
272

Sietse Ringers's avatar
Sietse Ringers committed
273
	sessionHelper(t, request, "issue", client)
274
275
	require.NotNil(t, client.Attributes(credid, 0))
	require.Nil(t, client.Attributes(credid, 1))
276

Sietse Ringers's avatar
Sietse Ringers committed
277
	sessionHelper(t, request, "issue", client)
278
279
	require.NotNil(t, client.Attributes(credid, 0))
	require.Nil(t, client.Attributes(credid, 1))
280
281
}

282
283
284
285
286
287
288
289
/* 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)
290
	defer test.ClearTestStorage(t)
291
292
293
294
295
296
297
	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
298
299
	request := getSpecialIssuanceRequest(false, "a23456789012345678901234567890")
	sessionHelper(t, request, "issue", client)
300
301
302
303
304
305
306

	/* 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
307
308
	request = getSpecialIssuanceRequest(false, "é")
	sessionHelper(t, request, "issue", client)
309
310
}

311
// Use the existing keyshare enrollment and credentials
Sietse Ringers's avatar
Sietse Ringers committed
312
313
314
315
// 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)
316
	defer test.ClearTestStorage(t)
Sietse Ringers's avatar
Sietse Ringers committed
317

318
	keyshareSessions(t, client)
Sietse Ringers's avatar
Sietse Ringers committed
319
}
320
321
322

func TestDisclosureNewAttributeUpdateSchemeManager(t *testing.T) {
	client := parseStorage(t)
323
	defer test.ClearTestStorage(t)
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347

	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)
348
349
	defer test.ClearTestStorage(t)

350
351
352
353
354
355
356
357
358
359
360
361
362
363
	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)
364
365
	defer test.ClearTestStorage(t)

366
367
368
369
370
371
372
373
374
375
376
377
	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
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
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)
}

418
419
420
421
422
// 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)
423
	defer test.ClearTestStorage(t)
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444

	// 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
445
446
	request := getCombinedIssuanceRequest(irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID"))
	sessionHelper(t, request, "issue", client)
447
448
449
450
451

	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"))

452
	basepath := test.FindTestdataFolder(t) + "/storage/test/irma_configuration/irma-demo"
453
454
455
456
457
458
459
460
461
462
	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)
}