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

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

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

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

var client *Client

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

Koen van Ingen's avatar
Koen van Ingen committed
29
	jwtcontents := getIssuanceJwt(name, true)
30
31
32
33
34
35
36
37
38
39
	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)

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

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

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

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

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

71
72
73
74
75
76
77
	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
78
		test.ClearTestStorage(t)
79
80
81
82
		t.Fatal(*err)
	}

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

95
96
97
98
99
100
101
102
103
104
105
106
// 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
107
		test.ClearTestStorage(t)
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
		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)
123
	client.NewManualSession(request, &ms)
124

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

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

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

142
143
144
145
146
147
148
149
150
	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
151
		test.ClearTestStorage(t)
152
153
		t.Fatal(*err)
	}
154

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

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

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

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

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

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

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

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

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

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

232
	// First, we need to issue an extra credential (BSN)
233
	is := ManualSessionHandler{t: t, errorChannel: make(chan *irma.SessionError)}
234
	go issue(t, is)
235
	if err := <-is.errorChannel; err != nil {
236
237
238
239
240
241
		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\"]}]}"
242

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

245
	client.NewManualSession(request, &ms)
246

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

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

269
270
271
272
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\"]}]}"
273
	ms := createManualSessionHandler(request, request, t)
274

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

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

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

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

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

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

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

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