issue.go 4.87 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package backend

import (
	"fmt"
	"strconv"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/go-errors/errors"
	"github.com/mhe/gabi"
	"github.com/privacybydesign/irmago"
	"github.com/privacybydesign/irmago/irmaserver"
)

func validateIssuanceRequest(request *irma.IssuanceRequest) error {
	for _, cred := range request.Credentials {
		// Check that we have the appropriate private key
		iss := cred.CredentialTypeID.IssuerIdentifier()
		privatekey, havekey := conf.PrivateKeys[iss]
		if !havekey {
			return fmt.Errorf("missing private key of issuer %s", iss.String())
		}
		pubkey, err := conf.IrmaConfiguration.PublicKey(iss, int(privatekey.Counter))
		if err != nil {
			return err
		}
		if pubkey == nil {
			return fmt.Errorf("missing public key of issuer %s", iss.String())
		}
		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
		defaultValidity := irma.Timestamp(time.Now().Add(6 * time.Hour))
		if cred.Validity == nil {
			cred.Validity = &defaultValidity
		}
		if cred.Validity.Before(irma.Timestamp(time.Now())) {
			return errors.New("cannot issue expired credentials")
		}
	}

	return nil
}

func (session *session) getProofP(commitments *gabi.IssueCommitmentMessage, scheme irma.SchemeManagerIdentifier) (*gabi.ProofP, error) {
	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())
		}
		claims := &struct {
			jwt.StandardClaims
			ProofP *gabi.ProofP
		}{}
		token, err := jwt.ParseWithClaims(str, claims, func(t *jwt.Token) (interface{}, error) {
			var kid int
			if kidstr, ok := t.Header["kid"].(string); ok {
				var err error
				if kid, err = strconv.Atoi(kidstr); err != nil {
					return nil, err
				}
			}
			return conf.IrmaConfiguration.KeyshareServerPublicKey(scheme, kid)
		})
		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
}

func (session *session) issue(commitments *gabi.IssueCommitmentMessage) (int, []byte, *irmaserver.SessionResult) {
	request := session.request.(*irma.IssuanceRequest)
	discloseCount := len(request.Disclose)
	if len(commitments.Proofs) != len(request.Credentials)+discloseCount {
		return failSession(session, irmaserver.ErrorAttributesMissing, "")
	}

	// Compute list of public keys against which to verify the received proofs
	disclosureproofs := irma.ProofList(commitments.Proofs[:discloseCount])
	pubkeys, err := disclosureproofs.ExtractPublicKeys(conf.IrmaConfiguration)
	if err != nil {
		return failSession(session, irmaserver.ErrorInvalidProofs, err.Error())
	}
	for _, cred := range request.Credentials {
		iss := cred.CredentialTypeID.IssuerIdentifier()
		pubkey, _ := conf.IrmaConfiguration.PublicKey(iss, cred.KeyCounter) // No error, already checked earlier
		pubkeys = append(pubkeys, pubkey)
	}

	// Verify and merge keyshare server proofs, if any
	for i, proof := range commitments.Proofs {
		pubkey := pubkeys[i]
		schemeid := irma.NewIssuerIdentifier(pubkey.Issuer).SchemeManagerIdentifier()
		if conf.IrmaConfiguration.SchemeManagers[schemeid].Distributed() {
			proofP, err := session.getProofP(commitments, schemeid)
			if err != nil {
				failSession(session, irmaserver.ErrorKeyshareProofMissing, err.Error())
			}
			proof.MergeProofP(proofP, pubkey)
		}
	}

	// Verify all proofs and check disclosed attributes, if any, against request
	session.disclosed, session.proofStatus = irma.ProofList(commitments.Proofs).VerifyAgainstDisjunctions(
		conf.IrmaConfiguration, request.Disclose, request.Context, request.Nonce, pubkeys, false)
	if session.proofStatus != irma.ProofStatusValid {
		return failSession(session, irmaserver.ErrorInvalidProofs, "")
	}

	// Compute CL signatures
	var sigs []*gabi.IssueSignatureMessage
	for i, cred := range request.Credentials {
		id := cred.CredentialTypeID.IssuerIdentifier()
		pk, _ := conf.IrmaConfiguration.PublicKey(id, cred.KeyCounter)
		issuer := gabi.NewIssuer(conf.PrivateKeys[id], pk, one)
		proof := commitments.Proofs[i+discloseCount].(*gabi.ProofU)
		attributes, err := cred.AttributeList(conf.IrmaConfiguration, 0x03)
		if err != nil {
			return failSession(session, irmaserver.ErrorUnknown, err.Error())
		}
		sig, err := issuer.IssueSignature(proof.U, attributes.Ints, commitments.Nonce2)
		if err != nil {
			return failSession(session, irmaserver.ErrorUnknown, err.Error())
		}
		sigs = append(sigs, sig)
	}

	s, b := responseJson(sigs)
	return s, b, finishSession(session)
}