manual_session_test.go 13.5 KB
Newer Older
1
2
3
package irmaclient

import (
4
	"encoding/json"
Koen van Ingen's avatar
Koen van Ingen committed
5
	"fmt"
6

Sietse Ringers's avatar
Sietse Ringers committed
7
8
	"testing"

9
	"github.com/go-errors/errors"
10
	"github.com/privacybydesign/irmago"
Koen van Ingen's avatar
Koen van Ingen committed
11
	"github.com/privacybydesign/irmago/internal/test"
12
13
14
15
16
)

type ManualSessionHandler struct {
	permissionHandler PermissionHandler
	pinHandler        PinHandler
Koen van Ingen's avatar
Koen van Ingen committed
17
	t                 *testing.T
18
	errorChannel      chan *irma.SessionError
19
	resultChannel     chan *irma.SignatureProofResult
20
21
	sigRequest        *irma.SignatureRequest // Request used to create signature
	sigVerifyRequest  *irma.SignatureRequest // Request used to verify signature
22
23
24
25
}

var client *Client

26
27
28
29
// Issue BSN credential using sessionHelper
func issue(t *testing.T, ms ManualSessionHandler) {
	name := "testip"

30
	jwtcontents := getIssuanceJwt(name, true, "")
31
32
33
34
35
36
	sessionHandlerHelper(t, jwtcontents, "issue", client, &ms)
}

// Flip one bit in the proof string if invalidate is set to true
var invalidate bool

37
38
func corruptAndConvertProofString(proof string) []byte {
	proofBytes := []byte(proof)
39
	if invalidate {
40
41
		// 42 because this is somewhere in a bigint in the json string
		proofBytes[42] ^= 0x01
42
	}
43
	return proofBytes
44
45
}

46
47
48
// Create a ManualSessionHandler for unit tests
func createManualSessionHandler(request string, invalidRequest string, t *testing.T) ManualSessionHandler {
	errorChannel := make(chan *irma.SessionError)
49
	resultChannel := make(chan *irma.SignatureProofResult)
50

51
	sigRequestJSON := []byte(request)
52
	invalidSigRequestJSON := []byte(invalidRequest)
53
	sigRequest := &irma.SignatureRequest{}
54
	invalidSigRequest := &irma.SignatureRequest{}
55
	json.Unmarshal(sigRequestJSON, sigRequest)
56
57
58
59
60
61
62
63
64
65
66
67
68
	json.Unmarshal(invalidSigRequestJSON, invalidSigRequest)

	return ManualSessionHandler{
		t:                t,
		errorChannel:     errorChannel,
		resultChannel:    resultChannel,
		sigRequest:       sigRequest,
		sigVerifyRequest: invalidSigRequest,
	}
}

func TestManualSession(t *testing.T) {
	invalidate = false
69

70
	request := "{\"nonce\": 42, \"context\": 1337, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
71
72
73
74
75
76
	ms := createManualSessionHandler(request, request, t)

	client = parseStorage(t)
	client.NewManualSession(request, &ms)

	if err := <-ms.errorChannel; err != nil {
Koen van Ingen's avatar
Koen van Ingen committed
77
		test.ClearTestStorage(t)
78
79
80
81
		t.Fatal(*err)
	}

	// No errors, obtain proof result from channel
82
	result := <-ms.resultChannel
83
84
	if ps := result.ProofStatus; ps != irma.VALID {
		t.Logf("Invalid proof result: %v Expected: %v", ps, irma.VALID)
85
		t.Fatal()
86
	}
87
	if attrStatus := result.ToAttributeResultList()[0].AttributeProofStatus; attrStatus != irma.PRESENT {
88
		t.Logf("Invalid attribute result value: %v Expected: %v", attrStatus, irma.PRESENT)
89
		t.Fail()
90
	}
91
92
	test.ClearTestStorage(t)
}
93

94
95
96
97
// Test if the session fails with unsatisfiable error if we cannot satify the signature request
func TestManualSessionUnsatisfiable(t *testing.T) {
	invalidate = false

98
	request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":{\"irma-demo.RU.studentCard.studentID\": \"123\"}}]}"
99
100
101
102
103
104
105
	ms := createManualSessionHandler(request, request, t)

	client = parseStorage(t)
	client.NewManualSession(request, &ms)

	// Fail test if we won't get UnsatisfiableRequest error
	if err := <-ms.errorChannel; err.ErrorType != irma.ErrorType("UnsatisfiableRequest") {
Koen van Ingen's avatar
Koen van Ingen committed
106
		test.ClearTestStorage(t)
107
108
109
110
111
112
113
114
115
		t.Fatal(*err)
	}
	test.ClearTestStorage(t)
}

// Test if proof verification fails with status 'ERROR_CRYPTO' if we verify it with an invalid nonce
func TestManualSessionInvalidNonce(t *testing.T) {
	invalidate = false

116
117
	request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
	invalidRequest := "{\"nonce\": 1, \"context\": 0, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
118
119
120
121

	ms := createManualSessionHandler(request, invalidRequest, t)

	client = parseStorage(t)
122
	client.NewManualSession(request, &ms)
123

124
	if err := <-ms.errorChannel; err != nil {
Koen van Ingen's avatar
Koen van Ingen committed
125
		test.ClearTestStorage(t)
126
127
		t.Fatal(*err)
	}
128

129
	// No errors, obtain proof result from channel
130
131
	if result := <-ms.resultChannel; result.ProofStatus != irma.UNMATCHED_REQUEST {
		t.Logf("Invalid proof result: %v Expected: %v", result.ProofStatus, irma.UNMATCHED_REQUEST)
132
133
		t.Fail()
	}
134
	test.ClearTestStorage(t)
135
136
}

137
138
// Test if proof verification fails with status 'MISSING_ATTRIBUTES' if we provide it with a non-matching signature request
func TestManualSessionInvalidRequest(t *testing.T) {
139
140
	invalidate = false

141
142
	request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
	invalidRequest := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.university\"]}]}"
143
144
145
146
147
148
149

	ms := createManualSessionHandler(request, invalidRequest, t)

	client = parseStorage(t)
	client.NewManualSession(request, &ms)

	if err := <-ms.errorChannel; err != nil {
Koen van Ingen's avatar
Koen van Ingen committed
150
		test.ClearTestStorage(t)
151
152
		t.Fatal(*err)
	}
153

154
	// No errors, obtain proof result from channel
155
	result := <-ms.resultChannel
156
157
	if ps := result.ProofStatus; ps != irma.MISSING_ATTRIBUTES {
		t.Logf("Invalid proof result: %v Expected: %v", ps, irma.MISSING_ATTRIBUTES)
158
159
		t.Fail()
	}
160
161

	// First attribute result is MISSING, because it is in the request but not disclosed
162
	if attrStatus := result.ToAttributeResultList()[0].AttributeProofStatus; attrStatus != irma.MISSING {
163
		t.Logf("Invalid attribute result value: %v Expected: %v", attrStatus, irma.MISSING)
164
165
		t.Fail()
	}
166
	// Second attribute result is EXTRA, since it is disclosed, but not matching the sigrequest
167
	if attrStatus := result.ToAttributeResultList()[1].AttributeProofStatus; attrStatus != irma.EXTRA {
168
		t.Logf("Invalid attribute result value: %v Expected: %v", attrStatus, irma.EXTRA)
169
		t.Fail()
170
	}
171
172
173
174
175
176
177
	test.ClearTestStorage(t)
}

// Test if proof verification fails with status 'MISSING_ATTRIBUTES' if we provide it with invalid attribute values
func TestManualSessionInvalidAttributeValue(t *testing.T) {
	invalidate = false

178
179
	request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":{\"irma-demo.RU.studentCard.studentID\": \"456\"}}]}"
	invalidRequest := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":{\"irma-demo.RU.studentCard.studentID\": \"123\"}}]}"
180
181

	ms := createManualSessionHandler(request, invalidRequest, t)
182
183

	client = parseStorage(t)
184
	client.NewManualSession(request, &ms)
185

186
	if err := <-ms.errorChannel; err != nil {
Koen van Ingen's avatar
Koen van Ingen committed
187
		test.ClearTestStorage(t)
188
189
190
191
		t.Fatal(*err)
	}

	// No errors, obtain proof result from channel
192
	result := <-ms.resultChannel
193
194
	if ps := result.ProofStatus; ps != irma.MISSING_ATTRIBUTES {
		t.Logf("Invalid proof result: %v Expected: %v", ps, irma.MISSING_ATTRIBUTES)
195
196
		t.Fail()
	}
197
	if attrStatus := result.ToAttributeResultList()[0].AttributeProofStatus; attrStatus != irma.INVALID_VALUE {
198
		t.Logf("Invalid attribute result value: %v Expected: %v", attrStatus, irma.INVALID_VALUE)
199
200
201
202
203
204
205
206
		t.Fail()
	}
	test.ClearTestStorage(t)
}

func TestManualKeyShareSession(t *testing.T) {
	invalidate = false

207
	request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"test.test.mijnirma.email\"]}]}"
208

209
210
211
212
213
214
	ms := createManualSessionHandler(request, request, t)

	client = parseStorage(t)
	client.NewManualSession(request, &ms)

	if err := <-ms.errorChannel; err != nil {
Koen van Ingen's avatar
Koen van Ingen committed
215
		test.ClearTestStorage(t)
216
217
		t.Fatal(*err)
	}
218
219

	// No errors, obtain proof result from channel
220
221
	if result := <-ms.resultChannel; result.ProofStatus != irma.VALID {
		t.Logf("Invalid proof result: %v Expected: %v", result.ProofStatus, irma.VALID)
222
223
		t.Fail()
	}
224
	test.ClearTestStorage(t)
225
226
227
228
}

func TestManualSessionMultiProof(t *testing.T) {
	invalidate = false
229
230
	client = parseStorage(t)

231
	// First, we need to issue an extra credential (BSN)
232
	is := ManualSessionHandler{t: t, errorChannel: make(chan *irma.SessionError)}
233
	go issue(t, is)
234
	if err := <-is.errorChannel; err != nil {
235
236
237
238
239
		fmt.Println("Error during initial issueing!")
		t.Fatal(*err)
	}

	// Request to sign with both BSN and StudentID
240
	request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]},{\"label\":\"BSN\",\"attributes\":[\"irma-demo.MijnOverheid.root.BSN\"]}]}"
241

242
	ms := createManualSessionHandler(request, request, t)
243

244
	client.NewManualSession(request, &ms)
245

246
	if err := <-ms.errorChannel; err != nil {
Koen van Ingen's avatar
Koen van Ingen committed
247
		test.ClearTestStorage(t)
248
249
		t.Fatal(*err)
	}
250
251

	// No errors, obtain proof result from channel
252
253
254
255
256
	result := <-ms.resultChannel
	if ps := result.ProofStatus; ps != irma.VALID {
		t.Logf("Invalid proof result: %v Expected: %v", result.ProofStatus, irma.VALID)
		t.Fail()
	}
257
	if attrStatus := result.ToAttributeResultList()[0].AttributeProofStatus; attrStatus != irma.PRESENT {
258
259
260
		t.Logf("Invalid attribute result value: %v Expected: %v", attrStatus, irma.PRESENT)
		t.Fail()
	}
261
	if attrStatus := result.ToAttributeResultList()[1].AttributeProofStatus; attrStatus != irma.PRESENT {
262
		t.Logf("Invalid attribute result value: %v Expected: %v", attrStatus, irma.PRESENT)
263
264
		t.Fail()
	}
265
	test.ClearTestStorage(t)
266
267
}

268
269
270
func TestManualSessionInvalidProof(t *testing.T) {
	invalidate = true

271
	request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
272
	ms := createManualSessionHandler(request, request, t)
273

274
	client = parseStorage(t)
275
276
	client.NewManualSession(request, &ms)

277
	if err := <-ms.errorChannel; err != nil {
Koen van Ingen's avatar
Koen van Ingen committed
278
		test.ClearTestStorage(t)
279
280
		t.Fatal(*err)
	}
281
282

	// No errors, obtain proof result from channel
283
284
	if result := <-ms.resultChannel; result.ProofStatus != irma.INVALID_CRYPTO {
		t.Logf("Invalid proof result: %v Expected: %v", result.ProofStatus, irma.INVALID_CRYPTO)
285
286
		t.Fail()
	}
287
	test.ClearTestStorage(t)
288
289
}

290
func (sh *ManualSessionHandler) Success(irmaAction irma.Action, result string) {
291
292
293
	switch irmaAction {
	case irma.ActionSigning:
		// Make proof corrupt if we want to test invalid proofs
294
295
296
297
298
299
300
301
302
303
		resultBytes := corruptAndConvertProofString(result)
		irmaSignedMessage := &irma.IrmaSignedMessage{}

		if err := json.Unmarshal(resultBytes, irmaSignedMessage); err != nil {
			sh.errorChannel <- &irma.SessionError{
				Err:       err,
				ErrorType: irma.ErrorSerialization,
			}
			return
		}
304

305
		go func() {
306
			sh.resultChannel <- irma.VerifySig(client.Configuration, irmaSignedMessage, sh.sigVerifyRequest)
307
		}()
308
	}
309
	sh.errorChannel <- nil
310
}
Koen van Ingen's avatar
Koen van Ingen committed
311
func (sh *ManualSessionHandler) UnsatisfiableRequest(irmaAction irma.Action, serverName string, missingAttributes irma.AttributeDisjunctionList) {
312
313
	// This function is called from main thread, which blocks go channel, so need go routine here
	go func() {
314
		sh.errorChannel <- &irma.SessionError{
315
316
317
			ErrorType: irma.ErrorType("UnsatisfiableRequest"),
		}
	}()
Koen van Ingen's avatar
Koen van Ingen committed
318
}
319

Koen van Ingen's avatar
Koen van Ingen committed
320
func (sh *ManualSessionHandler) StatusUpdate(irmaAction irma.Action, status irma.Status) {}
321

322
323
324
325
func (sh *ManualSessionHandler) RequestPin(remainingAttempts int, ph PinHandler) {
	ph(true, "12345")
}
func (sh *ManualSessionHandler) RequestSignaturePermission(request irma.SignatureRequest, requesterName string, ph PermissionHandler) {
326
327
328
329
330
	var attributes []*irma.AttributeIdentifier
	for _, cand := range request.Candidates {
		attributes = append(attributes, cand[0])
	}
	c := irma.DisclosureChoice{attributes}
331
332
	ph(true, &c)
}
333
334
335
func (sh *ManualSessionHandler) RequestIssuancePermission(request irma.IssuanceRequest, issuerName string, ph PermissionHandler) {
	ph(true, nil)
}
336

337
// These handlers should not be called, fail test if they are called
338
func (sh *ManualSessionHandler) Cancelled(irmaAction irma.Action) {
339
	sh.errorChannel <- &irma.SessionError{Err: errors.New("Session was cancelled")}
340
}
341
342
func (sh *ManualSessionHandler) MissingKeyshareEnrollment(manager irma.SchemeManagerIdentifier) {
	sh.errorChannel <- &irma.SessionError{Err: errors.Errorf("Missing keyshare server %s", manager.String())}
Koen van Ingen's avatar
Koen van Ingen committed
343
344
}
func (sh *ManualSessionHandler) RequestSchemeManagerPermission(manager *irma.SchemeManager, callback func(proceed bool)) {
345
	sh.errorChannel <- &irma.SessionError{Err: errors.New("Unexpected session type")}
Koen van Ingen's avatar
Koen van Ingen committed
346
347
}
func (sh *ManualSessionHandler) RequestVerificationPermission(request irma.DisclosureRequest, verifierName string, ph PermissionHandler) {
348
	sh.errorChannel <- &irma.SessionError{Err: errors.New("Unexpected session type")}
Koen van Ingen's avatar
Koen van Ingen committed
349
}
350
351
func (sh *ManualSessionHandler) Failure(irmaAction irma.Action, err *irma.SessionError) {
	fmt.Println(err.Err)
352
	sh.errorChannel <- err
353
}
Koen van Ingen's avatar
Koen van Ingen committed
354
355
356
357
358
359
360
361
362
func (sh *ManualSessionHandler) KeyshareBlocked(manager irma.SchemeManagerIdentifier, duration int) {
	sh.errorChannel <- &irma.SessionError{Err: errors.New("KeyshareBlocked")}
}
func (sh *ManualSessionHandler) KeyshareEnrollmentIncomplete(manager irma.SchemeManagerIdentifier) {
	sh.errorChannel <- &irma.SessionError{Err: errors.New("KeyshareEnrollmentIncomplete")}
}
func (sh *ManualSessionHandler) KeyshareEnrollmentMissing(manager irma.SchemeManagerIdentifier) {
	sh.errorChannel <- &irma.SessionError{Err: errors.Errorf("Missing keyshare server %s", manager.String())}
}
363
364
365
func (sh *ManualSessionHandler) KeyshareEnrollmentDeleted(manager irma.SchemeManagerIdentifier) {
	sh.errorChannel <- &irma.SessionError{Err: errors.Errorf("Keyshare enrollment deleted for %s", manager.String())}
}