manual_session_test.go 13.6 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"

Koen van Ingen's avatar
Koen van Ingen committed
30
	jwtcontents := getIssuanceJwt(name, true)
31
32
33
34
35
36
37
38
39
40
	sessionHandlerHelper(t, jwtcontents, "issue", client, &ms)
}

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

func corruptProofString(proof string) string {
	if invalidate {
		proofBytes := []byte(proof)

41
		// 15 because this is somewhere in a bigint in the json string
42
		proofBytes[15] ^= 0x01
43
44
45
46
47
		return string(proofBytes)
	}
	return proof
}

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

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

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

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

72
73
74
75
76
77
78
	request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"messageType\":\"STRING\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
	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
79
		test.ClearTestStorage(t)
80
81
82
83
		t.Fatal(*err)
	}

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

96
97
98
99
100
101
102
103
104
105
106
107
// Test if the session fails with unsatisfiable error if we cannot satify the signature request
func TestManualSessionUnsatisfiable(t *testing.T) {
	invalidate = false

	request := "{\"nonce\": 0, \"context\": 0, \"message\":\"I owe you everything\",\"messageType\":\"STRING\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":{\"irma-demo.RU.studentCard.studentID\": \"123\"}}]}"
	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
108
		test.ClearTestStorage(t)
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
		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

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

	ms := createManualSessionHandler(request, invalidRequest, t)

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

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

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

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

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

	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
152
		test.ClearTestStorage(t)
153
154
		t.Fatal(*err)
	}
155

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

	// First attribute result is MISSING, because it is in the request but not disclosed
164
	if attrStatus := result.ToAttributeResultList()[0].AttributeProofStatus; attrStatus != irma.MISSING {
165
		t.Logf("Invalid attribute result value: %v Expected: %v", attrStatus, irma.MISSING)
166
167
		t.Fail()
	}
168
	// Second attribute result is EXTRA, since it is disclosed, but not matching the sigrequest
169
	if attrStatus := result.ToAttributeResultList()[1].AttributeProofStatus; attrStatus != irma.EXTRA {
170
		t.Logf("Invalid attribute result value: %v Expected: %v", attrStatus, irma.EXTRA)
171
		t.Fail()
172
	}
173
174
175
176
177
178
179
180
181
182
183
	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

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

	ms := createManualSessionHandler(request, invalidRequest, t)
184
185

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

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

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

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

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

211
212
213
214
215
216
	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
217
		test.ClearTestStorage(t)
218
219
		t.Fatal(*err)
	}
220
221

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

func TestManualSessionMultiProof(t *testing.T) {
	invalidate = false
231
232
	client = parseStorage(t)

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

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

244
	ms := createManualSessionHandler(request, request, t)
245

246
	client.NewManualSession(request, &ms)
247

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

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

270
271
272
273
func TestManualSessionInvalidProof(t *testing.T) {
	invalidate = true

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

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

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

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

292
func (sh *ManualSessionHandler) Success(irmaAction irma.Action, result string) {
293
294
295
296
297
	switch irmaAction {
	case irma.ActionSigning:
		// Make proof corrupt if we want to test invalid proofs
		result = corruptProofString(result)

298
		go func() {
299
			sh.resultChannel <- irma.VerifySig(client.Configuration, result, sh.sigVerifyRequest)
300
		}()
301
	}
302
	sh.errorChannel <- nil
303
}
Koen van Ingen's avatar
Koen van Ingen committed
304
func (sh *ManualSessionHandler) UnsatisfiableRequest(irmaAction irma.Action, serverName string, missingAttributes irma.AttributeDisjunctionList) {
305
306
	// This function is called from main thread, which blocks go channel, so need go routine here
	go func() {
307
		sh.errorChannel <- &irma.SessionError{
308
309
310
			ErrorType: irma.ErrorType("UnsatisfiableRequest"),
		}
	}()
Koen van Ingen's avatar
Koen van Ingen committed
311
}
312

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

315
316
317
318
func (sh *ManualSessionHandler) RequestPin(remainingAttempts int, ph PinHandler) {
	ph(true, "12345")
}
func (sh *ManualSessionHandler) RequestSignaturePermission(request irma.SignatureRequest, requesterName string, ph PermissionHandler) {
319
320
321
322
323
	var attributes []*irma.AttributeIdentifier
	for _, cand := range request.Candidates {
		attributes = append(attributes, cand[0])
	}
	c := irma.DisclosureChoice{attributes}
324
325
	ph(true, &c)
}
326
327
328
func (sh *ManualSessionHandler) RequestIssuancePermission(request irma.IssuanceRequest, issuerName string, ph PermissionHandler) {
	ph(true, nil)
}
329

330
// These handlers should not be called, fail test if they are called
331
func (sh *ManualSessionHandler) Cancelled(irmaAction irma.Action) {
332
	sh.errorChannel <- &irma.SessionError{Err: errors.New("Session was cancelled")}
333
}
334
335
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
336
337
}
func (sh *ManualSessionHandler) RequestSchemeManagerPermission(manager *irma.SchemeManager, callback func(proceed bool)) {
338
	sh.errorChannel <- &irma.SessionError{Err: errors.New("Unexpected session type")}
Koen van Ingen's avatar
Koen van Ingen committed
339
340
}
func (sh *ManualSessionHandler) RequestVerificationPermission(request irma.DisclosureRequest, verifierName string, ph PermissionHandler) {
341
	sh.errorChannel <- &irma.SessionError{Err: errors.New("Unexpected session type")}
Koen van Ingen's avatar
Koen van Ingen committed
342
}
343
344
func (sh *ManualSessionHandler) Failure(irmaAction irma.Action, err *irma.SessionError) {
	fmt.Println(err.Err)
345
	sh.errorChannel <- err
346
}
Koen van Ingen's avatar
Koen van Ingen committed
347
348
349
350
351
352
353
354
355
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())}
}
356
357
358
func (sh *ManualSessionHandler) KeyshareEnrollmentDeleted(manager irma.SchemeManagerIdentifier) {
	sh.errorChannel <- &irma.SessionError{Err: errors.Errorf("Keyshare enrollment deleted for %s", manager.String())}
}