updates.go 4.47 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"
)

Sietse Ringers's avatar
Sietse Ringers committed
15
16
17
// This file contains the update mechanism for CredentialManager
// 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
28
29
30
}

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

32
33
34
35
36
37
// 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
Sietse Ringers's avatar
Sietse Ringers committed
38
	if cm.updates, err = cm.storage.LoadUpdates(); err != nil {
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
		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)
	}

Sietse Ringers's avatar
Sietse Ringers committed
57
	cm.storage.StoreUpdates(cm.updates)
58
59
60
61

	return nil
}

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

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

78
	bytes, err := ioutil.ReadFile(cardemuXML)
Sietse Ringers's avatar
Sietse Ringers committed
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
118
119
120
	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 {
121
		cm.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, cm.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
140
			if err = cm.addCredential(cred, false); err != nil {
				return
Sietse Ringers's avatar
Sietse Ringers committed
141
142
143
144
145
			}
		}
	}

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

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

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

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