handle.go 6.9 KB
Newer Older
1
package servercore
2
3

import (
4
	"github.com/privacybydesign/gabi"
5
	"github.com/privacybydesign/irmago"
Sietse Ringers's avatar
Sietse Ringers committed
6
	"github.com/privacybydesign/irmago/server"
7
	"github.com/sirupsen/logrus"
8
9
)

Sietse Ringers's avatar
Sietse Ringers committed
10
11
12
13
14
// This file contains the handler functions for the protocol messages, receiving and returning normally
// Go-typed messages here (JSON (un)marshalling is handled by the router).
// Maintaining the session state is done here, as well as checking whether the session is in the
// appropriate status before handling the request.

15
func (session *session) handleDelete() {
16
	if session.status.Finished() {
17
		return
18
	}
19
	session.markAlive()
Sietse Ringers's avatar
Sietse Ringers committed
20

21
	session.result = &server.SessionResult{Token: session.token, Status: server.StatusCancelled, Type: session.action}
Sietse Ringers's avatar
Sietse Ringers committed
22
	session.setStatus(server.StatusCancelled)
23
24
}

25
func (session *session) handleGetRequest(min, max *irma.ProtocolVersion) (irma.SessionRequest, *irma.RemoteError) {
Sietse Ringers's avatar
Sietse Ringers committed
26
27
	if session.status != server.StatusInitialized {
		return nil, server.RemoteError(server.ErrorUnexpectedRequest, "Session already started")
28
29
30
	}
	session.markAlive()

31
32
33
34
35
36
37
38
39
40
	logger := session.conf.Logger.WithFields(logrus.Fields{"session": session.token})

	// Handle legacy clients that do not support condiscon, by attempting to convert the condiscon
	// session request to the legacy session request format
	legacy, legacyErr := session.request.Legacy()
	session.legacyCompatible = legacyErr == nil
	if legacyErr != nil {
		logger.Info("Using condiscon: backwards compatibility with legacy IRMA apps is disabled")
	}

41
	var err error
42
	if session.version, err = session.chooseProtocolVersion(min, max); err != nil {
Sietse Ringers's avatar
Sietse Ringers committed
43
		return nil, session.fail(server.ErrorProtocolVersion, "")
44
	}
45
	logger.WithFields(logrus.Fields{"version": session.version.String()}).Debugf("Protocol version negotiated")
46
	session.request.Base().ProtocolVersion = session.version
47

Sietse Ringers's avatar
Sietse Ringers committed
48
	session.setStatus(server.StatusConnected)
49
50
51
52
53
54

	if session.version.Below(2, 5) {
		logger.Info("Returning legacy session format")
		legacy.Base().ProtocolVersion = session.version
		return legacy, nil
	}
55
	return session.request, nil
56
57
}

58
59
60
61
func (session *session) handleGetStatus() (server.Status, *irma.RemoteError) {
	return session.status, nil
}

62
func (session *session) handlePostSignature(signature *irma.SignedMessage) (*irma.ProofStatus, *irma.RemoteError) {
Sietse Ringers's avatar
Sietse Ringers committed
63
64
	if session.status != server.StatusConnected {
		return nil, server.RemoteError(server.ErrorUnexpectedRequest, "Session not yet started or already finished")
65
	}
66
	session.markAlive()
67

68
69
	var err error
	var rerr *irma.RemoteError
70
	session.result.Signature = signature
71
	session.result.Disclosed, session.result.ProofStatus, err = signature.Verify(
72
		session.conf.IrmaConfiguration, session.request.(*irma.SignatureRequest))
73
	if err == nil {
Sietse Ringers's avatar
Sietse Ringers committed
74
		session.setStatus(server.StatusDone)
75
76
	} else {
		if err == irma.ErrorMissingPublicKey {
Sietse Ringers's avatar
Sietse Ringers committed
77
			rerr = session.fail(server.ErrorUnknownPublicKey, err.Error())
78
		} else {
Sietse Ringers's avatar
Sietse Ringers committed
79
			rerr = session.fail(server.ErrorUnknown, err.Error())
80
81
82
		}
	}
	return &session.result.ProofStatus, rerr
83
}
84

85
func (session *session) handlePostDisclosure(disclosure *irma.Disclosure) (*irma.ProofStatus, *irma.RemoteError) {
Sietse Ringers's avatar
Sietse Ringers committed
86
87
	if session.status != server.StatusConnected {
		return nil, server.RemoteError(server.ErrorUnexpectedRequest, "Session not yet started or already finished")
88
	}
89
	session.markAlive()
90

91
92
	var err error
	var rerr *irma.RemoteError
Sietse Ringers's avatar
Sietse Ringers committed
93
	session.result.Disclosed, session.result.ProofStatus, err = disclosure.Verify(
94
		session.conf.IrmaConfiguration, session.request.(*irma.DisclosureRequest))
95
	if err == nil {
Sietse Ringers's avatar
Sietse Ringers committed
96
		session.setStatus(server.StatusDone)
97
98
	} else {
		if err == irma.ErrorMissingPublicKey {
Sietse Ringers's avatar
Sietse Ringers committed
99
			rerr = session.fail(server.ErrorUnknownPublicKey, err.Error())
100
		} else {
Sietse Ringers's avatar
Sietse Ringers committed
101
			rerr = session.fail(server.ErrorUnknown, err.Error())
102
103
104
		}
	}
	return &session.result.ProofStatus, rerr
105
106
}

Sietse Ringers's avatar
Sietse Ringers committed
107
func (session *session) handlePostCommitments(commitments *irma.IssueCommitmentMessage) ([]*gabi.IssueSignatureMessage, *irma.RemoteError) {
Sietse Ringers's avatar
Sietse Ringers committed
108
109
	if session.status != server.StatusConnected {
		return nil, server.RemoteError(server.ErrorUnexpectedRequest, "Session not yet started or already finished")
Sietse Ringers's avatar
Sietse Ringers committed
110
111
	}
	session.markAlive()
112

Sietse Ringers's avatar
Sietse Ringers committed
113
	request := session.request.(*irma.IssuanceRequest)
114

115
116
117
	discloseCount := len(commitments.Proofs) - len(request.Credentials)
	if discloseCount < 0 {
		return nil, session.fail(server.ErrorMalformedInput, "Received insufficient proofs")
Sietse Ringers's avatar
Sietse Ringers committed
118
	}
119

Sietse Ringers's avatar
Sietse Ringers committed
120
121
	// Compute list of public keys against which to verify the received proofs
	disclosureproofs := irma.ProofList(commitments.Proofs[:discloseCount])
122
	pubkeys, err := disclosureproofs.ExtractPublicKeys(session.conf.IrmaConfiguration)
Sietse Ringers's avatar
Sietse Ringers committed
123
	if err != nil {
124
		return nil, session.fail(server.ErrorMalformedInput, err.Error())
Sietse Ringers's avatar
Sietse Ringers committed
125
126
127
	}
	for _, cred := range request.Credentials {
		iss := cred.CredentialTypeID.IssuerIdentifier()
128
		pubkey, _ := session.conf.IrmaConfiguration.PublicKey(iss, cred.KeyCounter) // No error, already checked earlier
Sietse Ringers's avatar
Sietse Ringers committed
129
130
		pubkeys = append(pubkeys, pubkey)
	}
131

Sietse Ringers's avatar
Sietse Ringers committed
132
133
134
135
	// Verify and merge keyshare server proofs, if any
	for i, proof := range commitments.Proofs {
		pubkey := pubkeys[i]
		schemeid := irma.NewIssuerIdentifier(pubkey.Issuer).SchemeManagerIdentifier()
136
		if session.conf.IrmaConfiguration.SchemeManagers[schemeid].Distributed() {
Sietse Ringers's avatar
Sietse Ringers committed
137
138
			proofP, err := session.getProofP(commitments, schemeid)
			if err != nil {
Sietse Ringers's avatar
Sietse Ringers committed
139
				return nil, session.fail(server.ErrorKeyshareProofMissing, err.Error())
Sietse Ringers's avatar
Sietse Ringers committed
140
141
142
			}
			proof.MergeProofP(proofP, pubkey)
		}
143
144
	}

Sietse Ringers's avatar
Sietse Ringers committed
145
	// Verify all proofs and check disclosed attributes, if any, against request
Sietse Ringers's avatar
Sietse Ringers committed
146
	session.result.Disclosed, session.result.ProofStatus, err = commitments.Disclosure().VerifyAgainstDisjunctions(
147
		session.conf.IrmaConfiguration, request.Disclose, request.GetContext(), request.GetNonce(nil), pubkeys, false)
148
149
	if err != nil {
		if err == irma.ErrorMissingPublicKey {
Sietse Ringers's avatar
Sietse Ringers committed
150
			return nil, session.fail(server.ErrorUnknownPublicKey, "")
151
		} else {
Sietse Ringers's avatar
Sietse Ringers committed
152
			return nil, session.fail(server.ErrorUnknown, "")
153
154
155
		}
	}
	if session.result.ProofStatus == irma.ProofStatusExpired {
Sietse Ringers's avatar
Sietse Ringers committed
156
		return nil, session.fail(server.ErrorAttributesExpired, "")
157
	}
Sietse Ringers's avatar
Sietse Ringers committed
158
	if session.result.ProofStatus != irma.ProofStatusValid {
Sietse Ringers's avatar
Sietse Ringers committed
159
		return nil, session.fail(server.ErrorInvalidProofs, "")
160
	}
Sietse Ringers's avatar
Sietse Ringers committed
161
162
163
164
165

	// Compute CL signatures
	var sigs []*gabi.IssueSignatureMessage
	for i, cred := range request.Credentials {
		id := cred.CredentialTypeID.IssuerIdentifier()
166
167
		pk, _ := session.conf.IrmaConfiguration.PublicKey(id, cred.KeyCounter)
		sk, _ := session.conf.PrivateKey(id)
168
		issuer := gabi.NewIssuer(sk, pk, one)
169
170
171
172
		proof, ok := commitments.Proofs[i+discloseCount].(*gabi.ProofU)
		if !ok {
			return nil, session.fail(server.ErrorMalformedInput, "Received invalid issuance commitment")
		}
173
		attributes, err := cred.AttributeList(session.conf.IrmaConfiguration, 0x03)
Sietse Ringers's avatar
Sietse Ringers committed
174
		if err != nil {
Sietse Ringers's avatar
Sietse Ringers committed
175
			return nil, session.fail(server.ErrorIssuanceFailed, err.Error())
Sietse Ringers's avatar
Sietse Ringers committed
176
177
178
		}
		sig, err := issuer.IssueSignature(proof.U, attributes.Ints, commitments.Nonce2)
		if err != nil {
Sietse Ringers's avatar
Sietse Ringers committed
179
			return nil, session.fail(server.ErrorIssuanceFailed, err.Error())
Sietse Ringers's avatar
Sietse Ringers committed
180
181
		}
		sigs = append(sigs, sig)
182
	}
Sietse Ringers's avatar
Sietse Ringers committed
183

Sietse Ringers's avatar
Sietse Ringers committed
184
	session.setStatus(server.StatusDone)
Sietse Ringers's avatar
Sietse Ringers committed
185
	return sigs, nil
186
}