updates.go 4.59 KB
Newer Older
Sietse Ringers's avatar
Sietse Ringers committed
1
2
3
4
5
6
7
8
package irmago

import (
	"encoding/json"
	"encoding/xml"
	"html"
	"io/ioutil"
	"math/big"
9
	"time"
Sietse Ringers's avatar
Sietse Ringers committed
10

Sietse Ringers's avatar
Sietse Ringers committed
11
	"github.com/go-errors/errors"
Sietse Ringers's avatar
Sietse Ringers committed
12
13
14
	"github.com/mhe/gabi"
)

15
// This file contains the update mechanism for Client
Sietse Ringers's avatar
Sietse Ringers committed
16
17
// as well as updates themselves.

18
type update struct {
19
20
21
22
	When    Timestamp
	Number  int
	Success bool
	Error   *string
23
24
}

25
26
27
var clientUpdates = []func(client *Client) error{
	func(client *Client) error {
		_, err := client.ParseAndroidStorage()
28
29
30
		return err
	},
}
31

32
// update performs any function from clientUpdates that has not
33
34
// already been executed in the past, keeping track of previously executed updates
// in the file at updatesFile.
35
func (client *Client) update() error {
36
37
	// Load and parse file containing info about already performed updates
	var err error
38
	if client.updates, err = client.storage.LoadUpdates(); err != nil {
39
40
41
42
		return err
	}

	// Perform all new updates
43
44
	for i := len(client.updates); i < len(clientUpdates); i++ {
		err = clientUpdates[i](client)
Sietse Ringers's avatar
Sietse Ringers committed
45
		u := update{
46
47
48
49
50
51
			When:    Timestamp(time.Now()),
			Number:  i,
			Success: err == nil,
		}
		if err != nil {
			str := err.Error()
Sietse Ringers's avatar
Sietse Ringers committed
52
			u.Error = &str
53
		}
54
		client.updates = append(client.updates, u)
55
56
	}

57
	return client.storage.StoreUpdates(client.updates)
58
59
}

Sietse Ringers's avatar
Sietse Ringers committed
60
61
62
63
// ParseAndroidStorage parses an Android cardemu.xml shared preferences file
// from the old Android IRMA app, parsing its credentials into the current instance,
// and saving them to storage.
// CAREFUL: this method overwrites any existing secret keys and attributes on storage.
64
65
func (client *Client) ParseAndroidStorage() (present bool, err error) {
	if client.androidStoragePath == "" {
66
67
68
		return false, nil
	}

69
	cardemuXML := client.androidStoragePath + "/shared_prefs/cardemu.xml"
70
71
	present, err = PathExists(cardemuXML)
	if err != nil || !present {
Sietse Ringers's avatar
Sietse Ringers committed
72
73
		return
	}
74
	present = true
Sietse Ringers's avatar
Sietse Ringers committed
75

76
	bytes, err := ioutil.ReadFile(cardemuXML)
Sietse Ringers's avatar
Sietse Ringers committed
77
78
79
80
81
82
83
84
85
	if err != nil {
		return
	}
	parsedxml := struct {
		Strings []struct {
			Name    string `xml:"name,attr"`
			Content string `xml:",chardata"`
		} `xml:"string"`
	}{}
Sietse Ringers's avatar
Sietse Ringers committed
86
87
88
	if err = xml.Unmarshal(bytes, &parsedxml); err != nil {
		return
	}
Sietse Ringers's avatar
Sietse Ringers committed
89
90
91
92
93
94
95

	parsedjson := make(map[string][]*struct {
		Signature    *gabi.CLSignature `json:"signature"`
		Pk           *gabi.PublicKey   `json:"-"`
		Attributes   []*big.Int        `json:"attributes"`
		SharedPoints []*big.Int        `json:"public_sks"`
	})
96
	client.keyshareServers = make(map[SchemeManagerIdentifier]*keyshareServer)
Sietse Ringers's avatar
Sietse Ringers committed
97
98
99
100
101
102
103
104
105
	for _, xmltag := range parsedxml.Strings {
		if xmltag.Name == "credentials" {
			jsontag := html.UnescapeString(xmltag.Content)
			if err = json.Unmarshal([]byte(jsontag), &parsedjson); err != nil {
				return
			}
		}
		if xmltag.Name == "keyshare" {
			jsontag := html.UnescapeString(xmltag.Content)
106
			if err = json.Unmarshal([]byte(jsontag), &client.keyshareServers); err != nil {
Sietse Ringers's avatar
Sietse Ringers committed
107
108
109
110
111
112
113
114
115
				return
			}
		}
		if xmltag.Name == "KeyshareKeypairs" {
			jsontag := html.UnescapeString(xmltag.Content)
			keys := make([]*paillierPrivateKey, 0, 3)
			if err = json.Unmarshal([]byte(jsontag), &keys); err != nil {
				return
			}
116
			client.paillierKeyCache = keys[0]
Sietse Ringers's avatar
Sietse Ringers committed
117
118
119
120
		}
	}

	for _, list := range parsedjson {
121
		client.secretkey = &secretKey{Key: list[0].Attributes[0]}
Sietse Ringers's avatar
Sietse Ringers committed
122
123
124
125
126
127
128
129
		for _, oldcred := range list {
			gabicred := &gabi.Credential{
				Attributes: oldcred.Attributes,
				Signature:  oldcred.Signature,
			}
			if oldcred.SharedPoints != nil && len(oldcred.SharedPoints) > 0 {
				gabicred.Signature.KeyshareP = oldcred.SharedPoints[0]
			}
130
			var cred *credential
131
			if cred, err = newCredential(gabicred, client.ConfigurationStore); err != nil {
132
				return
133
			}
Sietse Ringers's avatar
Sietse Ringers committed
134
			if cred.CredentialType() == nil {
135
136
				err = errors.New("cannot add unknown credential type")
				return
Sietse Ringers's avatar
Sietse Ringers committed
137
138
			}

139
			if err = client.addCredential(cred, false); err != nil {
140
				return
Sietse Ringers's avatar
Sietse Ringers committed
141
142
143
144
			}
		}
	}

145
146
	if len(client.credentials) > 0 {
		if err = client.storage.StoreAttributes(client.attributes); err != nil {
147
			return
Sietse Ringers's avatar
Sietse Ringers committed
148
		}
149
		if err = client.storage.StoreSecretKey(client.secretkey); err != nil {
150
			return
Sietse Ringers's avatar
Sietse Ringers committed
151
152
153
		}
	}

154
155
	if len(client.keyshareServers) > 0 {
		if err = client.storage.StoreKeyshareServers(client.keyshareServers); err != nil {
156
			return
Sietse Ringers's avatar
Sietse Ringers committed
157
158
		}
	}
159
	client.UnenrolledSchemeManagers = client.unenrolledSchemeManagers()
Sietse Ringers's avatar
Sietse Ringers committed
160

161
	if err = client.storage.StorePaillierKeys(client.paillierKeyCache); err != nil {
162
		return
Sietse Ringers's avatar
Sietse Ringers committed
163
	}
164
165
	if client.paillierKeyCache == nil {
		client.paillierKey(false) // trigger calculating a new one
Sietse Ringers's avatar
Sietse Ringers committed
166
167
	}

168
	if err = client.ConfigurationStore.Copy(client.androidStoragePath+"/app_store/irma_configuration", false); err != nil {
169
170
171
		return
	}
	// Copy from assets again to ensure we have the latest versions
172
	return present, client.ConfigurationStore.Copy(client.irmaConfigurationPath, true)
Sietse Ringers's avatar
Sietse Ringers committed
173
}