irmago_test.go 5.74 KB
Newer Older
Sietse Ringers's avatar
Sietse Ringers committed
1
package irmago
2
3

import (
4
5
	"fmt"
	"math/big"
Sietse Ringers's avatar
Sietse Ringers committed
6
	"os"
7
8
	"testing"
	"time"
Sietse Ringers's avatar
Sietse Ringers committed
9

10
11
	"encoding/json"

12
	"github.com/stretchr/testify/assert"
Sietse Ringers's avatar
Sietse Ringers committed
13
	"github.com/stretchr/testify/require"
14
15
)

Sietse Ringers's avatar
Sietse Ringers committed
16
func TestMain(m *testing.M) {
Sietse Ringers's avatar
Sietse Ringers committed
17
18
	retCode := m.Run()

Sietse Ringers's avatar
Sietse Ringers committed
19
20
	err := os.RemoveAll("testdata/storage/test")
	if err != nil {
Sietse Ringers's avatar
Sietse Ringers committed
21
		fmt.Println("Could not delete test storage")
Sietse Ringers's avatar
Sietse Ringers committed
22
23
24
		os.Exit(1)
	}

Sietse Ringers's avatar
Sietse Ringers committed
25
26
	os.Exit(retCode)
}
Sietse Ringers's avatar
Sietse Ringers committed
27

Sietse Ringers's avatar
Sietse Ringers committed
28
29
30
31
32
33
34
35
36
func parseMetaStore(t *testing.T) {
	require.NoError(t, MetaStore.ParseFolder("testdata/irma_configuration"), "MetaStore.ParseFolder() failed")
}

func parseStorage(t *testing.T) {
	exists, err := pathExists("testdata/storage/path")
	require.NoError(t, err, "pathexists() failed")
	if !exists {
		require.NoError(t, os.Mkdir("testdata/storage/test", 0755), "Could not create test storage")
Sietse Ringers's avatar
Sietse Ringers committed
37
	}
Sietse Ringers's avatar
Sietse Ringers committed
38
	require.NoError(t, Manager.Init("testdata/storage/test"), "Manager.Init() failed")
39

Sietse Ringers's avatar
Sietse Ringers committed
40
41
42
43
44
}

func teardown(t *testing.T) {
	MetaStore = newConfigurationStore()
	Manager = newCredentialManager()
45
	assert.NoError(t, os.RemoveAll("testdata/storage/test"))
Sietse Ringers's avatar
Sietse Ringers committed
46
47
}

48
49
50
51
52
53
54
// A convenience function for initializing big integers from known correct (10
// base) strings. Use with care, errors are ignored.
func s2big(s string) (r *big.Int) {
	r, _ = new(big.Int).SetString(s, 10)
	return
}

Sietse Ringers's avatar
Sietse Ringers committed
55
func parseAndroidStorage(t *testing.T) {
56
	assert.NoError(t, Manager.ParseAndroidStorage(), "ParseAndroidStorage() failed")
Sietse Ringers's avatar
Sietse Ringers committed
57
58
59
}

func verifyStoreIsUnmarshaled(t *testing.T) {
60
	cred, err := Manager.Credential(NewCredentialTypeIdentifier("irma-demo.RU.studentCard"), 0)
61
62
63
	assert.NoError(t, err, "could not fetch credential")
	assert.NotNil(t, cred, "Credential should exist")
	assert.NotNil(t, cred.Attributes[0], "Metadata attribute of irma-demo.RU.studentCard should not be nil")
64

65
	assert.True(t,
Sietse Ringers's avatar
Sietse Ringers committed
66
		cred.Signature.Verify(cred.PublicKey(), cred.Attributes),
67
68
69
		"Credential should be valid",
	)
}
Sietse Ringers's avatar
Sietse Ringers committed
70
71

func TestAndroidParse(t *testing.T) {
Sietse Ringers's avatar
Sietse Ringers committed
72
73
	parseMetaStore(t)
	parseStorage(t)
Sietse Ringers's avatar
Sietse Ringers committed
74
75
	parseAndroidStorage(t)
	verifyStoreIsUnmarshaled(t)
Sietse Ringers's avatar
Sietse Ringers committed
76
77

	teardown(t)
Sietse Ringers's avatar
Sietse Ringers committed
78
79
80
}

func TestUnmarshaling(t *testing.T) {
Sietse Ringers's avatar
Sietse Ringers committed
81
82
	parseMetaStore(t)
	parseStorage(t)
Sietse Ringers's avatar
Sietse Ringers committed
83
84
85
86
87
88
	parseAndroidStorage(t)

	Manager = newCredentialManager()
	Manager.Init("testdata/storage/test")

	verifyStoreIsUnmarshaled(t)
Sietse Ringers's avatar
Sietse Ringers committed
89
90

	teardown(t)
Sietse Ringers's avatar
Sietse Ringers committed
91
}
92
93

func TestParseStore(t *testing.T) {
Sietse Ringers's avatar
Sietse Ringers committed
94
	parseMetaStore(t)
95

96
97
	assert.NotNil(t, MetaStore.Issuers[NewIssuerIdentifier("irma-demo.RU")].CurrentPublicKey().N, "irma-demo.RU public key has no modulus")
	assert.Equal(t,
98
		"Irma Demo",
99
		MetaStore.SchemeManagers[NewSchemeManagerIdentifier("irma-demo")].Name.Translation("en"),
100
		"irma-demo scheme manager has unexpected name")
101
	assert.Equal(t,
102
		"Radboud Universiteit Nijmegen",
103
		MetaStore.Issuers[NewIssuerIdentifier("irma-demo.RU")].Name.Translation("en"),
104
		"irma-demo.RU issuer has unexpected name")
105
	assert.Equal(t,
106
		"Student Card",
107
		MetaStore.Credentials[NewCredentialTypeIdentifier("irma-demo.RU.studentCard")].ShortName.Translation("en"),
108
109
		"irma-demo.RU.studentCard has unexpected name")

110
	assert.Equal(t,
111
		"studentID",
112
		MetaStore.Credentials[NewCredentialTypeIdentifier("irma-demo.RU.studentCard")].Attributes[2].ID,
113
114
115
116
		"irma-demo.RU.studentCard.studentID has unexpected name")

	// Hash algorithm pseudocode:
	// Base64(SHA256("irma-demo.RU.studentCard")[0:16])
117
	assert.Contains(t, MetaStore.reverseHashes, "1stqlPad5edpfS1Na1U+DA==",
118
		"irma-demo.RU.studentCard had improper hash")
119
	assert.Contains(t, MetaStore.reverseHashes, "CLjnADMBYlFcuGOT7Z0xRg==",
120
		"irma-demo.MijnOverheid.root had improper hash")
Sietse Ringers's avatar
Sietse Ringers committed
121
122

	teardown(t)
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
}

func TestMetadataAttribute(t *testing.T) {
	metadata := NewMetadataAttribute()
	if metadata.Version() != 0x02 {
		t.Errorf("Unexpected metadata version: %d", metadata.Version())
	}

	expiry := metadata.SigningDate().Unix() + int64(metadata.ValidityDuration()*ExpiryFactor)
	if !time.Unix(expiry, 0).Equal(metadata.Expiry()) {
		t.Errorf("Invalid signing date")
	}

	if metadata.KeyCounter() != 0 {
		t.Errorf("Unexpected key counter")
	}
}

func TestMetadataCompatibility(t *testing.T) {
Sietse Ringers's avatar
Sietse Ringers committed
142
	parseMetaStore(t)
143
144
145

	// An actual metadata attribute of an IRMA credential extracted from the IRMA app
	attr := MetadataFromInt(s2big("49043481832371145193140299771658227036446546573739245068"))
146
	assert.NotNil(t, attr.CredentialType(), "attr.CredentialType() should not be nil")
147

148
	assert.Equal(t,
149
		NewCredentialTypeIdentifier("irma-demo.RU.studentCard"),
150
151
152
		attr.CredentialType().Identifier(),
		"Metadata credential type was not irma-demo.RU.studentCard",
	)
153
154
155
156
	assert.Equal(t, byte(0x02), attr.Version(), "Unexpected metadata version")
	assert.Equal(t, time.Unix(1499904000, 0), attr.SigningDate(), "Unexpected signing date")
	assert.Equal(t, time.Unix(1516233600, 0), attr.Expiry(), "Unexpected expiry date")
	assert.Equal(t, 2, attr.KeyCounter(), "Unexpected key counter")
Sietse Ringers's avatar
Sietse Ringers committed
157
158

	teardown(t)
159
}
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201

func TestAttributeDisjunctionMarshaling(t *testing.T) {
	disjunction := AttributeDisjunction{}

	var _ json.Unmarshaler = &disjunction
	var _ json.Marshaler = &disjunction

	id := NewAttributeTypeIdentifier("MijnOverheid.ageLower.over18")

	attrsjson := `
	{
		"label": "Over 18",
		"attributes": {
			"MijnOverheid.ageLower.over18": "yes",
			"Thalia.age.over18": "Yes"
		}
	}`
	require.NoError(t, json.Unmarshal([]byte(attrsjson), &disjunction))
	require.True(t, disjunction.HasValues())
	require.Contains(t, disjunction.Attributes, id)
	require.Contains(t, disjunction.Values, id)
	require.Equal(t, disjunction.Values[id], "yes")

	disjunction = AttributeDisjunction{}
	attrsjson = `
	{
		"label": "Over 18",
		"attributes": [
			"MijnOverheid.ageLower.over18",
			"Thalia.age.over18"
		]
	}`
	require.NoError(t, json.Unmarshal([]byte(attrsjson), &disjunction))
	require.False(t, disjunction.HasValues())
	require.Contains(t, disjunction.Attributes, id)

	require.True(t, disjunction.MatchesStore())

	require.False(t, disjunction.Satisfied())
	disjunction.selected = &disjunction.Attributes[0]
	require.True(t, disjunction.Satisfied())
}