helpers.go 5.63 KB
Newer Older
1
package core
Sietse Ringers's avatar
Sietse Ringers committed
2
3

import (
4
	"encoding/json"
5
	"net/http"
6
	"reflect"
Sietse Ringers's avatar
Sietse Ringers committed
7
8
9
10
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/go-errors/errors"
11
	"github.com/privacybydesign/gabi"
Sietse Ringers's avatar
Sietse Ringers committed
12
	"github.com/privacybydesign/irmago"
Sietse Ringers's avatar
Sietse Ringers committed
13
	"github.com/privacybydesign/irmago/server"
14
	"gopkg.in/antage/eventsource.v1"
Sietse Ringers's avatar
Sietse Ringers committed
15
16
17
18
19
20
)

// Session helpers

func (session *session) markAlive() {
	session.lastActive = time.Now()
21
	conf.Logger.Debugf("session %s marked active at %s", session.token, session.lastActive.String())
Sietse Ringers's avatar
Sietse Ringers committed
22
23
}

Sietse Ringers's avatar
Sietse Ringers committed
24
func (session *session) setStatus(status server.Status) {
25
	conf.Logger.Debugf("Status of session %s updated from %s to %s", session.token, session.status, status)
Sietse Ringers's avatar
Sietse Ringers committed
26
	session.status = status
Sietse Ringers's avatar
Sietse Ringers committed
27
	session.result.Status = status
28
29
30
31
32
33
34
35
	sessions.update(session)
}

func (session *session) onUpdate() {
	if session.evtSource != nil {
		conf.Logger.Tracef("Sending %s to SSE listeners of session %s", session.status, session.token)
		session.evtSource.SendEventMessage(string(session.status), "", "")
	}
Sietse Ringers's avatar
Sietse Ringers committed
36
37
}

Sietse Ringers's avatar
Sietse Ringers committed
38
39
40
41
func (session *session) fail(err server.Error, message string) *irma.RemoteError {
	rerr := server.RemoteError(err, message)
	session.setStatus(server.StatusCancelled)
	session.result = &server.SessionResult{Err: rerr, Token: session.token, Status: server.StatusCancelled}
Sietse Ringers's avatar
Sietse Ringers committed
42
43
44
45
46
47
48
49
50
	return rerr
}

// Issuance helpers

func validateIssuanceRequest(request *irma.IssuanceRequest) error {
	for _, cred := range request.Credentials {
		// Check that we have the appropriate private key
		iss := cred.CredentialTypeID.IssuerIdentifier()
51
52
53
54
55
		privatekey, err := privatekey(iss)
		if err != nil {
			return err
		}
		if privatekey == nil {
56
			return errors.Errorf("missing private key of issuer %s", iss.String())
Sietse Ringers's avatar
Sietse Ringers committed
57
58
59
60
61
62
		}
		pubkey, err := conf.IrmaConfiguration.PublicKey(iss, int(privatekey.Counter))
		if err != nil {
			return err
		}
		if pubkey == nil {
63
			return errors.Errorf("missing public key of issuer %s", iss.String())
Sietse Ringers's avatar
Sietse Ringers committed
64
65
66
67
68
69
70
71
72
		}
		cred.KeyCounter = int(privatekey.Counter)

		// Check that the credential is consistent with irma_configuration
		if err := cred.Validate(conf.IrmaConfiguration); err != nil {
			return err
		}

		// Ensure the credential has an expiry date
73
		defaultValidity := irma.Timestamp(time.Now().AddDate(0, 6, 0))
Sietse Ringers's avatar
Sietse Ringers committed
74
75
76
77
78
79
80
81
82
83
84
		if cred.Validity == nil {
			cred.Validity = &defaultValidity
		}
		if cred.Validity.Before(irma.Timestamp(time.Now())) {
			return errors.New("cannot issue expired credentials")
		}
	}

	return nil
}

85
86
87
88
89
90
91
92
93
94
func privatekey(id irma.IssuerIdentifier) (sk *gabi.PrivateKey, err error) {
	sk = conf.IssuerPrivateKeys[id]
	if sk == nil {
		if sk, err = conf.IrmaConfiguration.PrivateKey(id); err != nil {
			return nil, err
		}
	}
	return sk, nil
}

Sietse Ringers's avatar
Sietse Ringers committed
95
func (session *session) getProofP(commitments *irma.IssueCommitmentMessage, scheme irma.SchemeManagerIdentifier) (*gabi.ProofP, error) {
Sietse Ringers's avatar
Sietse Ringers committed
96
97
98
99
100
101
102
103
104
	if session.kssProofs == nil {
		session.kssProofs = make(map[irma.SchemeManagerIdentifier]*gabi.ProofP)
	}

	if _, contains := session.kssProofs[scheme]; !contains {
		str, contains := commitments.ProofPjwts[scheme.Name()]
		if !contains {
			return nil, errors.Errorf("no keyshare proof included for scheme %s", scheme.Name())
		}
105
		conf.Logger.Trace("Parsing keyshare ProofP JWT: ", str)
Sietse Ringers's avatar
Sietse Ringers committed
106
107
108
109
		claims := &struct {
			jwt.StandardClaims
			ProofP *gabi.ProofP
		}{}
110
		token, err := jwt.ParseWithClaims(str, claims, conf.IrmaConfiguration.KeyshareServerKeyFunc(scheme))
Sietse Ringers's avatar
Sietse Ringers committed
111
112
113
114
115
116
117
118
119
120
121
122
		if err != nil {
			return nil, err
		}
		if !token.Valid {
			return nil, errors.Errorf("invalid keyshare proof included for scheme %s", scheme.Name())
		}
		session.kssProofs[scheme] = claims.ProofP
	}

	return session.kssProofs[scheme], nil
}

123
124
125
126
127
128
129
130
131
132
133
134
var eventHeaders = [][]byte{[]byte("Access-Control-Allow-Origin: *")}

func (session *session) eventSource() eventsource.EventSource {
	if session.evtSource != nil {
		return session.evtSource
	}

	conf.Logger.Trace("Making server sent event source for session ", session.token)
	session.evtSource = eventsource.New(nil, func(_ *http.Request) [][]byte { return eventHeaders })
	return session.evtSource
}

Sietse Ringers's avatar
Sietse Ringers committed
135
136
137
// Other

func chooseProtocolVersion(min, max *irma.ProtocolVersion) (*irma.ProtocolVersion, error) {
138
	if min.AboveVersion(maxProtocolVersion) || max.BelowVersion(minProtocolVersion) || max.BelowVersion(min) {
139
		return nil, server.LogWarning(errors.Errorf("Protocol version negotiation failed, min=%s max=%s", min.String(), max.String()))
Sietse Ringers's avatar
Sietse Ringers committed
140
141
142
143
144
145
146
	}
	if max.AboveVersion(maxProtocolVersion) {
		return maxProtocolVersion, nil
	} else {
		return max, nil
	}
}
147
148

// logPurgedRequest logs the request excluding any attribute values.
149
func logPurgedRequest(request irma.RequestorRequest) {
150
151
152
153
	// We want to log as much as possible of the request, but no attribute values.
	// We cannot just remove them from the request parameter as that would break the calling code.
	// So we create a deep copy of the request from which we can then safely remove whatever we want to.
	// Ugly hack alert: the easiest way to do this seems to be to convert it to JSON and then back.
154
	// As we do not know the precise type of request, we use reflection to create a new instance
155
156
157
158
159
160
	// of the same type as request, into which we then unmarshal our copy.
	cpy := reflect.New(reflect.TypeOf(request).Elem()).Interface()
	bts, _ := json.Marshal(request)
	_ = json.Unmarshal(bts, cpy)

	// Remove required attribute values from any attributes to be disclosed
161
	attrs := cpy.(irma.RequestorRequest).SessionRequest().ToDisclose()
162
163
164
165
	for _, disjunction := range attrs {
		disjunction.Values = nil
	}
	// Remove attribute values from attributes to be issued
166
167
	if isreq, ok := cpy.(*irma.IdentityProviderRequest); ok {
		for _, cred := range isreq.Request.Credentials {
168
169
170
171
172
173
			cred.Attributes = nil
		}
	}
	// Convert back to JSON to log
	conf.Logger.Info("Session request (purged of attribute values): ", server.ToJson(cpy))
}