manual_session_test.go 12.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

7
	"github.com/go-errors/errors"
8
	"github.com/privacybydesign/irmago/internal/test"
9
10
	"github.com/privacybydesign/irmago"
	"testing"
11
12
)

13
14
15
16
17
type Result struct {
	proofStatus ProofStatus
	attributes  *irma.AttributeResultList
}

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

var client *Client

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// Issue BSN credential using sessionHelper
func issue(t *testing.T, ms ManualSessionHandler) {
	name := "testip"

	jwtcontents := getIssuanceJwt(name)
	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)

		flipLoc := 15
		if proofBytes[flipLoc] == 0x33 {
			proofBytes[flipLoc] = 0x32
		} else {
			proofBytes[flipLoc] = 0x33
		}
		return string(proofBytes)
	}
	return proof
}

56
57
58
// Create a ManualSessionHandler for unit tests
func createManualSessionHandler(request string, invalidRequest string, t *testing.T) ManualSessionHandler {
	errorChannel := make(chan *irma.SessionError)
59
	resultChannel := make(chan Result)
60

61
	sigRequestJSON := []byte(request)
62
	invalidSigRequestJSON := []byte(invalidRequest)
63
	sigRequest := &irma.SignatureRequest{}
64
	invalidSigRequest := &irma.SignatureRequest{}
65
	json.Unmarshal(sigRequestJSON, sigRequest)
66
67
68
69
70
71
72
73
74
75
76
77
78
	json.Unmarshal(invalidSigRequestJSON, invalidSigRequest)

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

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

80
81
82
83
84
85
86
87
88
89
90
91
	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 {
	  test.ClearTestStorage(t)
		t.Fatal(*err)
	}

	// No errors, obtain proof result from channel
92
93
94
95
96
97
98
	result := <-ms.resultChannel
	if ps := result.proofStatus; ps != VALID {
		t.Logf("Invalid proof result: %v Expected: %v", ps, VALID)
		t.Fail()
	}
	if attrStatus := result.attributes.AttributeResults[0].AttributeProofStatus; attrStatus != irma.PRESENT {
		t.Logf("Invalid attribute result value: %v Expected: %v", attrStatus, irma.PRESENT)
99
		t.Fail()
100
	}
101
102
	test.ClearTestStorage(t)
}
103

104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// 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") {
	  test.ClearTestStorage(t)
		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)
132
	client.NewManualSession(request, &ms)
133

134
	if err := <-ms.errorChannel; err != nil {
135
	  test.ClearTestStorage(t)
136
137
		t.Fatal(*err)
	}
138

139
	// No errors, obtain proof result from channel
140
141
	if result := <-ms.resultChannel; result.proofStatus != INVALID_CRYPTO {
		t.Logf("Invalid proof result: %v Expected: %v", result.proofStatus, INVALID_CRYPTO)
142
143
		t.Fail()
	}
144
	test.ClearTestStorage(t)
145
146
}

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

151
152
153
154
155
156
157
158
159
160
161
162
	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 {
	  test.ClearTestStorage(t)
		t.Fatal(*err)
	}
163

164
	// No errors, obtain proof result from channel
165
166
167
168
169
170
171
172
173
174
175
176
177
	result := <-ms.resultChannel
	if ps := result.proofStatus; ps != MISSING_ATTRIBUTES {
		t.Logf("Invalid proof result: %v Expected: %v", ps, MISSING_ATTRIBUTES)
		t.Fail()
	}
	// First attribute result is UNKOWN, since it is disclosed, but not matching the sigrequest
	if attrStatus := result.attributes.AttributeResults[0].AttributeProofStatus; attrStatus != irma.UNKNOWN {
		t.Logf("Invalid attribute result value: %v Expected: %v", attrStatus, irma.UNKNOWN)
		t.Fail()
	}
	// Second attribute result is MISSING, because it is in the request but not disclosed
	if attrStatus := result.attributes.AttributeResults[1].AttributeProofStatus; attrStatus != irma.MISSING {
		t.Logf("Invalid attribute result value: %v Expected: %v", attrStatus, irma.MISSING)
178
		t.Fail()
179
	}
180
181
182
183
184
185
186
187
188
189
190
	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)
191
192

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

195
196
197
198
199
200
	if err := <-ms.errorChannel; err != nil {
	  test.ClearTestStorage(t)
		t.Fatal(*err)
	}

	// No errors, obtain proof result from channel
201
202
203
204
205
206
207
	result := <-ms.resultChannel
	if ps := result.proofStatus; ps != MISSING_ATTRIBUTES {
		t.Logf("Invalid proof result: %v Expected: %v", ps, MISSING_ATTRIBUTES)
		t.Fail()
	}
	if attrStatus := result.attributes.AttributeResults[0].AttributeProofStatus; attrStatus != irma.INVALID_VALUE {
		t.Logf("Invalid attribute result value: %v Expected: %v", attrStatus, irma.INVALID_VALUE)
208
209
210
211
212
213
214
215
216
		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\"]}]}"
217

218
219
220
221
222
223
	ms := createManualSessionHandler(request, request, t)

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

	if err := <-ms.errorChannel; err != nil {
224
	  test.ClearTestStorage(t)
225
226
		t.Fatal(*err)
	}
227
228

	// No errors, obtain proof result from channel
229
230
	if result := <-ms.resultChannel; result.proofStatus != VALID {
		t.Logf("Invalid proof result: %v Expected: %v", result.proofStatus, VALID)
231
232
		t.Fail()
	}
233
	test.ClearTestStorage(t)
234
235
236
237
}

func TestManualSessionMultiProof(t *testing.T) {
	invalidate = false
238
239
	client = parseStorage(t)

240
	// First, we need to issue an extra credential (BSN)
241
	is := ManualSessionHandler{t: t, errorChannel: make(chan *irma.SessionError)}
242
	go issue(t, is)
243
	if err := <-is.errorChannel; err != nil {
244
245
246
247
248
249
		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\"]}]}"
250

251
	ms := createManualSessionHandler(request, request, t)
252

253
	client.NewManualSession(request, &ms)
254

255
	if err := <-ms.errorChannel; err != nil {
256
	  test.ClearTestStorage(t)
257
258
		t.Fatal(*err)
	}
259
260

	// No errors, obtain proof result from channel
261
262
	if result := <-ms.resultChannel; result.proofStatus != VALID {
		t.Logf("Invalid proof result: %v Expected: %v", result.proofStatus, VALID)
263
264
		t.Fail()
	}
265
	test.ClearTestStorage(t)
266
267
}

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

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

277
278
	if err := <-ms.errorChannel; err != nil {
	  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 != INVALID_CRYPTO {
		t.Logf("Invalid proof result: %v Expected: %v", result.proofStatus, 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
294
295
	switch irmaAction {
	case irma.ActionSigning:
		// Make proof corrupt if we want to test invalid proofs
		result = corruptProofString(result)

296
		go func() {
297
298
			proofStatus, attributeResultList := VerifySig(client.Configuration, result, sh.sigVerifyRequest)
			sh.resultChannel <- Result{proofStatus, attributeResultList}
299
		}()
300
	}
301
	sh.errorChannel <- nil
302
}
303
304
305
func (sh *ManualSessionHandler) UnsatisfiableRequest(irmaAction irma.Action, missingAttributes irma.AttributeDisjunctionList) {
	// 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
}