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

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

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

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

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

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

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

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

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

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

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

	return req
}

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

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

Sietse Ringers's avatar
Sietse Ringers committed
112
func getJwt(t *testing.T, request irma.SessionRequest, url string) string {
113
	var jwtcontents irma.RequestorJwt
Sietse Ringers's avatar
Sietse Ringers committed
114
115
116
117
118
119
120
	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))
121
122
	}

123
124
125
	skbts, err := ioutil.ReadFile(filepath.Join(test.FindTestdataFolder(t), "jwtkeys", "testrequestor-sk.pem"))
	require.NoError(t, err)
	sk, err := jwt.ParseRSAPrivateKeyFromPEM(skbts)
126
	require.NoError(t, err)
127
128
129
	tok := jwt.NewWithClaims(jwt.SigningMethodRS256, jwtcontents)
	tok.Header["kid"] = "testrequestor"
	j, err := tok.SignedString(sk)
130
131
	require.NoError(t, err)

132
	return j
Sietse Ringers's avatar
Sietse Ringers committed
133
134
}

135
func sessionHelper(t *testing.T, request irma.SessionRequest, sessiontype string, client *irmaclient.Client) {
Sietse Ringers's avatar
Sietse Ringers committed
136
137
138
139
140
	if client == nil {
		client = parseStorage(t)
		defer test.ClearTestStorage(t)
	}

141
142
143
144
145
146
147
148
	//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

	url := "http://localhost:48682"
	server := irma.NewHTTPTransport(url)
149
	var qr irma.Qr
150
	err := server.Post("create", &qr, getJwt(t, request, sessiontype))
151
	require.NoError(t, err)
152
153
	token := qr.URL
	qr.URL = url + "/irma/" + qr.URL
154
155
156

	c := make(chan *SessionResult)
	h := TestHandler{t, c, client}
157
	qrjson, err := json.Marshal(qr)
158
	require.NoError(t, err)
159
	client.NewSession(string(qrjson), h)
160
161
162
163

	if result := <-c; result != nil {
		require.NoError(t, result.Err)
	}
164
165
166
167
168
169
170

	bts, err := server.GetBytes("getproof/" + token)
	require.NoError(t, err)
	fmt.Println(string(bts))
	bts, err = server.GetBytes("result/" + token)
	require.NoError(t, err)
	fmt.Println(string(bts))
171
172
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

367
368
369
370
371
372
373
374
375
376
377
378
	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
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
418
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)
}

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

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

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

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