updates.go 4.38 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
type update struct {
16
17
18
19
	When    Timestamp
	Number  int
	Success bool
	Error   *string
20
21
22
23
24
25
26
27
}

var credentialManagerUpdates = []func(manager *CredentialManager) error{
	func(manager *CredentialManager) error {
		_, err := manager.ParseAndroidStorage()
		return err
	},
}
28

29
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
56
57
58
// update performs any function from credentialManagerUpdates that has not
// already been executed in the past, keeping track of previously executed updates
// in the file at updatesFile.
func (cm *CredentialManager) update() error {
	// Load and parse file containing info about already performed updates
	var err error
	if cm.updates, err = cm.storage.loadUpdates(); err != nil {
		return err
	}

	// Perform all new updates
	for i := len(cm.updates); i < len(credentialManagerUpdates); i++ {
		err = credentialManagerUpdates[i](cm)
		update := update{
			When:    Timestamp(time.Now()),
			Number:  i,
			Success: err == nil,
		}
		if err != nil {
			str := err.Error()
			update.Error = &str
		}
		cm.updates = append(cm.updates, update)
	}

	cm.storage.storeUpdates(cm.updates)

	return nil
}

Sietse Ringers's avatar
Sietse Ringers committed
59
60
61
62
// 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.
63
func (cm *CredentialManager) ParseAndroidStorage() (present bool, err error) {
64
65
66
67
68
	if cm.androidStoragePath == "" {
		return false, nil
	}

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

75
	bytes, err := ioutil.ReadFile(cardemuXML)
Sietse Ringers's avatar
Sietse Ringers committed
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
	if err != nil {
		return
	}
	parsedxml := struct {
		Strings []struct {
			Name    string `xml:"name,attr"`
			Content string `xml:",chardata"`
		} `xml:"string"`
	}{}
	xml.Unmarshal(bytes, &parsedxml)

	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"`
	})
	cm.keyshareServers = make(map[SchemeManagerIdentifier]*keyshareServer)
	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)
			if err = json.Unmarshal([]byte(jsontag), &cm.keyshareServers); err != nil {
				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
			}
			cm.paillierKeyCache = keys[0]
		}
	}

	for _, list := range parsedjson {
118
		cm.secretkey = &secretKey{Key: list[0].Attributes[0]}
Sietse Ringers's avatar
Sietse Ringers committed
119
120
121
122
123
124
125
126
		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]
			}
127
			var cred *credential
128
			if cred, err = newCredential(gabicred, cm.ConfigurationStore); err != nil {
129
				return
130
			}
Sietse Ringers's avatar
Sietse Ringers committed
131
			if cred.CredentialType() == nil {
132
133
				err = errors.New("cannot add unknown credential type")
				return
Sietse Ringers's avatar
Sietse Ringers committed
134
135
			}

136
137
			if err = cm.addCredential(cred, false); err != nil {
				return
Sietse Ringers's avatar
Sietse Ringers committed
138
139
140
141
142
			}
		}
	}

	if len(cm.credentials) > 0 {
143
		if err = cm.storage.storeAttributes(cm.attributes); err != nil {
144
			return
Sietse Ringers's avatar
Sietse Ringers committed
145
		}
146
		if err = cm.storage.storeSecretKey(cm.secretkey); err != nil {
147
			return
Sietse Ringers's avatar
Sietse Ringers committed
148
149
150
151
		}
	}

	if len(cm.keyshareServers) > 0 {
152
		if err = cm.storage.storeKeyshareServers(cm.keyshareServers); err != nil {
153
			return
Sietse Ringers's avatar
Sietse Ringers committed
154
155
156
		}
	}

157
	if err = cm.storage.storePaillierKeys(cm.paillierKeyCache); err != nil {
158
		return
Sietse Ringers's avatar
Sietse Ringers committed
159
160
161
162
163
	}
	if cm.paillierKeyCache == nil {
		cm.paillierKey(false) // trigger calculating a new one
	}

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