manager.go 3.2 KB
Newer Older
1
2
3
package irmago

import (
4
5
	"encoding/json"
	"encoding/xml"
6
	"errors"
7
	"html"
8
9
10
11
12
13
	"io/ioutil"
	"math/big"

	"github.com/mhe/gabi"
)

14
15
16
17
18
// Manager is the global instance of CredentialManager.
var Manager = CredentialManager{
	attributes:  make(map[string][]*AttributeList),
	credentials: make(map[string][]*gabi.Credential),
}
19
20
21
22
23

// CredentialManager manages credentials.
type CredentialManager struct {
	secretkey   *big.Int
	storagePath string
24
25
	attributes  map[string][]*AttributeList
	credentials map[string][]*gabi.Credential
26
27
}

28
29
30
func (cm *CredentialManager) generateSecretKey() (err error) {
	cm.secretkey, err = gabi.RandomBigInt(gabi.DefaultSystemParameters[1024].Lm)
	return
31
32
}

33
34
35
// Init deserializes the credentials from storage.
func (cm *CredentialManager) Init(path string) (err error) {
	cm.storagePath = path
36

37
38
39
	cm.ensureStorageExists()

	bytes, err := ioutil.ReadFile(cm.path(skFile))
40
41
42
	if err != nil {
		return
	}
43
	cm.secretkey = new(big.Int).SetBytes(bytes)
44

45
46
47
48
49
50
51
52
	return
}

// ParseAndroidStorage parses an Android cardemu.xml shared preferences file
// from the old Android IRMA app, parsing its credentials into the current instance.
func (cm *CredentialManager) ParseAndroidStorage() (err error) {
	exists, err := pathExists(cm.path(cardemuXML))
	if err != nil || !exists {
53
54
		return
	}
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

	bytes, err := ioutil.ReadFile(cm.path(cardemuXML))
	parsed := struct {
		Strings []struct {
			Name    string `xml:"name,attr"`
			Content string `xml:",chardata"`
		} `xml:"string"`
	}{}
	xml.Unmarshal(bytes, &parsed)

	for _, xmltag := range parsed.Strings {
		if xmltag.Name == "credentials" {
			jsontag := html.UnescapeString(xmltag.Content)
			if err = json.Unmarshal([]byte(jsontag), &cm.credentials); err != nil {
				return
			}
71
72
73
		}
	}

74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
	for _, list := range cm.credentials {
		if list != nil && len(list) > 0 {
			cm.secretkey = list[0].Attributes[0]
		}
		for i, cred := range list {
			// TODO move this metadata initialisation somehow into gabi.Credential?
			cred.MetadataAttribute = gabi.MetadataFromInt(cred.Attributes[1])
			if cred.CredentialType() == nil {
				return errors.New("cannot add unknown credential type")
			}

			cm.addCredential(cred)
			err = cm.storeSignature(cred, i)
			if err != nil {
				return err
			}
		}
91
	}
92
93
94

	if len(cm.credentials) > 0 {
		err = cm.storeAttributes()
95
		if err != nil {
96
			return err
97
		}
98
		err = cm.storeKey()
99
		if err != nil {
100
			return err
101
102
103
		}
	}

104
	return
105
106
}

107
108
109
110
111
112
func (cm *CredentialManager) addCredential(cred *gabi.Credential) {
	id := cred.CredentialType().Identifier()
	if _, exists := cm.attributes[id]; !exists {
		cm.attributes[id] = make([]*AttributeList, 0, 1)
	}
	cm.attributes[id] = append(cm.attributes[id], NewAttributeListFromInts(cred.Attributes[1:]))
113

114
115
116
117
118
	if _, exists := cm.credentials[id]; !exists {
		cm.credentials[id] = make([]*gabi.Credential, 0, 1)
	}
	cm.credentials[id] = append(cm.credentials[id], cred)
}
119

120
121
122
123
124
125
126
127
128
129
// Add adds the specified credential to the CredentialManager.
func (cm *CredentialManager) Add(cred *gabi.Credential) (err error) {
	if cred.CredentialType() == nil {
		return errors.New("cannot add unknown credential type")
	}

	cm.addCredential(cred)
	counter := len(cm.credentials) - 1

	err = cm.storeSignature(cred, counter)
130
131
132
	if err != nil {
		return
	}
133
	err = cm.storeAttributes()
134
135
	return
}