requests.go 11 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
package irmago

import (
	"crypto/sha256"
	"encoding/asn1"
	"fmt"
	"log"
	"math/big"
	"strconv"
	"time"

Sietse Ringers's avatar
Sietse Ringers committed
12
	"github.com/go-errors/errors"
Sietse Ringers's avatar
Sietse Ringers committed
13
14
	"github.com/mhe/gabi"
)
15

Sietse Ringers's avatar
Sietse Ringers committed
16
// SessionRequest contains the context and nonce for an IRMA session.
17
type SessionRequest struct {
18
19
	Context     *big.Int `json:"context"`
	Nonce       *big.Int `json:"nonce"`
20
	Candidates  [][]*AttributeIdentifier
21
22
	choice      *DisclosureChoice
	identifiers *IrmaIdentifierSet
Sietse Ringers's avatar
Sietse Ringers committed
23
24
}

25
26
27
28
func (sr *SessionRequest) SetCandidates(candidates [][]*AttributeIdentifier) {
	sr.Candidates = candidates
}

29
// DisclosureChoice returns the attributes to be disclosed in this session.
Sietse Ringers's avatar
Sietse Ringers committed
30
31
32
33
func (sr *SessionRequest) DisclosureChoice() *DisclosureChoice {
	return sr.choice
}

34
// SetDisclosureChoice sets the attributes to be disclosed in this session.
Sietse Ringers's avatar
Sietse Ringers committed
35
36
func (sr *SessionRequest) SetDisclosureChoice(choice *DisclosureChoice) {
	sr.choice = choice
37
38
}

Sietse Ringers's avatar
Sietse Ringers committed
39
// A DisclosureRequest is a request to disclose certain attributes.
40
41
42
43
44
type DisclosureRequest struct {
	SessionRequest
	Content AttributeDisjunctionList `json:"content"`
}

Sietse Ringers's avatar
Sietse Ringers committed
45
// A SignatureRequest is a a request to sign a message with certain attributes.
46
47
48
49
50
51
type SignatureRequest struct {
	DisclosureRequest
	Message     string `json:"message"`
	MessageType string `json:"messageType"`
}

Sietse Ringers's avatar
Sietse Ringers committed
52
53
// An IssuanceRequest is a request to issue certain credentials,
// optionally also asking for certain attributes to be simultaneously disclosed.
54
55
type IssuanceRequest struct {
	SessionRequest
56
57
58
	Credentials        []*CredentialRequest     `json:"credentials"`
	Disclose           AttributeDisjunctionList `json:"disclose"`
	CredentialInfoList CredentialInfoList       `json:",omitempty"`
Sietse Ringers's avatar
Sietse Ringers committed
59
60

	state *issuanceState
61
62
}

Sietse Ringers's avatar
Sietse Ringers committed
63
64
// A CredentialRequest contains the attributes and metadata of a credential
// that will be issued in an IssuanceRequest.
65
type CredentialRequest struct {
66
67
68
69
	Validity         *Timestamp                `json:"validity"`
	KeyCounter       int                       `json:"keyCounter"`
	CredentialTypeID *CredentialTypeIdentifier `json:"credential"`
	Attributes       map[string]string         `json:"attributes"`
Sietse Ringers's avatar
Sietse Ringers committed
70
71
}

Sietse Ringers's avatar
Sietse Ringers committed
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
// ServerJwt contains standard JWT fields.
type ServerJwt struct {
	Type       string    `json:"sub"`
	ServerName string    `json:"iss"`
	IssuedAt   Timestamp `json:"iat"`
}

// A ServiceProviderRequest contains a disclosure request.
type ServiceProviderRequest struct {
	Request *DisclosureRequest `json:"request"`
}

// A SignatureRequestorRequest contains a signing request.
type SignatureRequestorRequest struct {
	Request *SignatureRequest `json:"request"`
}

// An IdentityProviderRequest contains an issuance request.
type IdentityProviderRequest struct {
	Request *IssuanceRequest `json:"request"`
}

// ServiceProviderJwt is a requestor JWT for a disclosure session.
type ServiceProviderJwt struct {
	ServerJwt
	Request ServiceProviderRequest `json:"sprequest"`
}

// SignatureRequestorJwt is a requestor JWT for a signing session.
type SignatureRequestorJwt struct {
	ServerJwt
	Request SignatureRequestorRequest `json:"absrequest"`
}

// IdentityProviderJwt is a requestor JWT for issuance session.
type IdentityProviderJwt struct {
	ServerJwt
	Request IdentityProviderRequest `json:"iprequest"`
}

112
113
114
115
116
117
// IrmaSession is an IRMA session.
type IrmaSession interface {
	GetNonce() *big.Int
	SetNonce(*big.Int)
	GetContext() *big.Int
	SetContext(*big.Int)
118
	ToDisclose() AttributeDisjunctionList
119
120
	DisclosureChoice() *DisclosureChoice
	SetDisclosureChoice(choice *DisclosureChoice)
121
	SetCandidates(candidates [][]*AttributeIdentifier)
122
123
124
	Identifiers() *IrmaIdentifierSet
}

Sietse Ringers's avatar
Sietse Ringers committed
125
126
127
128
129
130
131
132
// Timestamp is a time.Time that marshals to Unix timestamps.
type Timestamp time.Time

type issuanceState struct {
	nonce2   *big.Int
	builders []*gabi.CredentialBuilder
}

133
134
135
136
137
138
139
140
func (cr *CredentialRequest) Info(store *ConfigurationStore) (*CredentialInfo, error) {
	list, err := cr.AttributeList(store)
	if err != nil {
		return nil, err
	}
	return NewCredentialInfo(list.Ints, store), nil
}

Sietse Ringers's avatar
Sietse Ringers committed
141
// AttributeList returns the list of attributes from this credential request.
142
func (cr *CredentialRequest) AttributeList(store *ConfigurationStore) (*AttributeList, error) {
Sietse Ringers's avatar
Sietse Ringers committed
143
144
	meta := NewMetadataAttribute()
	meta.setKeyCounter(cr.KeyCounter)
145
	meta.setCredentialTypeIdentifier(cr.CredentialTypeID.String())
Sietse Ringers's avatar
Sietse Ringers committed
146
147
148
149
150
151
152
	meta.setSigningDate()
	err := meta.setExpiryDate(cr.Validity)
	if err != nil {
		return nil, err
	}

	attrs := make([]*big.Int, len(cr.Attributes)+1, len(cr.Attributes)+1)
153
	credtype := store.CredentialTypes[*cr.CredentialTypeID]
Sietse Ringers's avatar
Sietse Ringers committed
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
	if credtype == nil {
		return nil, errors.New("Unknown credential type")
	}
	if len(credtype.Attributes) != len(cr.Attributes) {
		return nil, errors.New("Received unexpected amount of attributes")
	}

	attrs[0] = meta.Int
	for i, attrtype := range credtype.Attributes {
		if str, present := cr.Attributes[attrtype.ID]; present {
			attrs[i+1] = new(big.Int).SetBytes([]byte(str))
		} else {
			return nil, errors.New("Unknown attribute")
		}
	}

170
	return NewAttributeListFromInts(attrs, store), nil
Sietse Ringers's avatar
Sietse Ringers committed
171
172
}

Sietse Ringers's avatar
Cleanup    
Sietse Ringers committed
173
func newIssuanceState() (*issuanceState, error) {
Sietse Ringers's avatar
Sietse Ringers committed
174
175
176
177
178
179
180
181
	nonce2, err := gabi.RandomBigInt(gabi.DefaultSystemParameters[4096].Lstatzk)
	if err != nil {
		return nil, err
	}
	return &issuanceState{
		nonce2:   nonce2,
		builders: []*gabi.CredentialBuilder{},
	}, nil
182
183
}

184
185
186
187
188
189
190
func (dr *IssuanceRequest) Identifiers() *IrmaIdentifierSet {
	if dr.identifiers == nil {
		dr.identifiers = &IrmaIdentifierSet{
			SchemeManagers:  map[SchemeManagerIdentifier]struct{}{},
			Issuers:         map[IssuerIdentifier]struct{}{},
			CredentialTypes: map[CredentialTypeIdentifier]struct{}{},
			PublicKeys:      map[IssuerIdentifier][]int{},
Sietse Ringers's avatar
Sietse Ringers committed
191
192
		}

193
		for _, credreq := range dr.Credentials {
Sietse Ringers's avatar
Sietse Ringers committed
194
			issuer := credreq.CredentialTypeID.IssuerIdentifier()
195
196
			dr.identifiers.SchemeManagers[issuer.SchemeManagerIdentifier()] = struct{}{}
			dr.identifiers.Issuers[issuer] = struct{}{}
Sietse Ringers's avatar
Sietse Ringers committed
197
			dr.identifiers.CredentialTypes[*credreq.CredentialTypeID] = struct{}{}
198
199
200
201
202
203
204
205
			if dr.identifiers.PublicKeys[issuer] == nil {
				dr.identifiers.PublicKeys[issuer] = []int{}
			}
			dr.identifiers.PublicKeys[issuer] = append(dr.identifiers.PublicKeys[issuer], credreq.KeyCounter)
		}

		for _, disjunction := range dr.Disclose {
			for _, attr := range disjunction.Attributes {
Sietse Ringers's avatar
Sietse Ringers committed
206
207
208
209
210
211
212
213
214
				var cti CredentialTypeIdentifier
				if !attr.IsCredential() {
					cti = attr.CredentialTypeIdentifier()
				} else {
					cti = NewCredentialTypeIdentifier(attr.String())
				}
				dr.identifiers.SchemeManagers[cti.IssuerIdentifier().SchemeManagerIdentifier()] = struct{}{}
				dr.identifiers.Issuers[cti.IssuerIdentifier()] = struct{}{}
				dr.identifiers.CredentialTypes[cti] = struct{}{}
215
			}
Sietse Ringers's avatar
Sietse Ringers committed
216
217
		}
	}
218
	return dr.identifiers
Sietse Ringers's avatar
Sietse Ringers committed
219
220
}

Sietse Ringers's avatar
Sietse Ringers committed
221
222
// ToDisclose returns the attributes that must be disclosed in this issuance session.
func (ir *IssuanceRequest) ToDisclose() AttributeDisjunctionList { return ir.Disclose }
Sietse Ringers's avatar
Sietse Ringers committed
223
224
225
226
227
228
229
230
231
232
233
234
235

// GetContext returns the context of this session.
func (ir *IssuanceRequest) GetContext() *big.Int { return ir.Context }

// SetContext sets the context of this session.
func (ir *IssuanceRequest) SetContext(context *big.Int) { ir.Context = context }

// GetNonce returns the nonce of this session.
func (ir *IssuanceRequest) GetNonce() *big.Int { return ir.Nonce }

// SetNonce sets the nonce of this session.
func (ir *IssuanceRequest) SetNonce(nonce *big.Int) { ir.Nonce = nonce }

236
237
238
239
240
241
242
func (dr *DisclosureRequest) Identifiers() *IrmaIdentifierSet {
	if dr.identifiers == nil {
		dr.identifiers = &IrmaIdentifierSet{
			SchemeManagers:  map[SchemeManagerIdentifier]struct{}{},
			Issuers:         map[IssuerIdentifier]struct{}{},
			CredentialTypes: map[CredentialTypeIdentifier]struct{}{},
			PublicKeys:      map[IssuerIdentifier][]int{},
Sietse Ringers's avatar
Sietse Ringers committed
243
		}
244
245
246
247
248
249
		for _, disjunction := range dr.Content {
			for _, attr := range disjunction.Attributes {
				dr.identifiers.SchemeManagers[attr.CredentialTypeIdentifier().IssuerIdentifier().SchemeManagerIdentifier()] = struct{}{}
				dr.identifiers.Issuers[attr.CredentialTypeIdentifier().IssuerIdentifier()] = struct{}{}
				dr.identifiers.CredentialTypes[attr.CredentialTypeIdentifier()] = struct{}{}
			}
Sietse Ringers's avatar
Sietse Ringers committed
250
251
		}
	}
252
	return dr.identifiers
Sietse Ringers's avatar
Sietse Ringers committed
253
254
}

Sietse Ringers's avatar
Sietse Ringers committed
255
256
// ToDisclose returns the attributes to be disclosed in this session.
func (dr *DisclosureRequest) ToDisclose() AttributeDisjunctionList { return dr.Content }
Sietse Ringers's avatar
Sietse Ringers committed
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271

// GetContext returns the context of this session.
func (dr *DisclosureRequest) GetContext() *big.Int { return dr.Context }

// SetContext sets the context of this session.
func (dr *DisclosureRequest) SetContext(context *big.Int) { dr.Context = context }

// GetNonce returns the nonce of this session.
func (dr *DisclosureRequest) GetNonce() *big.Int { return dr.Nonce }

// SetNonce sets the nonce of this session.
func (dr *DisclosureRequest) SetNonce(nonce *big.Int) { dr.Nonce = nonce }

// GetNonce returns the nonce of this signature session
// (with the message already hashed into it).
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
func (sr *SignatureRequest) GetNonce() *big.Int {
	hashbytes := sha256.Sum256([]byte(sr.Message))
	hashint := new(big.Int).SetBytes(hashbytes[:])
	// TODO the 2 should be abstracted away
	asn1bytes, err := asn1.Marshal([]interface{}{big.NewInt(2), sr.Nonce, hashint})
	if err != nil {
		log.Print(err) // TODO? does this happen?
	}
	asn1hash := sha256.Sum256(asn1bytes)
	return new(big.Int).SetBytes(asn1hash[:])
}

// MarshalJSON marshals a timestamp.
func (t *Timestamp) MarshalJSON() ([]byte, error) {
	ts := time.Time(*t).Unix()
	stamp := fmt.Sprint(ts)
	return []byte(stamp), nil
}

// UnmarshalJSON unmarshals a timestamp.
func (t *Timestamp) UnmarshalJSON(b []byte) error {
	ts, err := strconv.Atoi(string(b))
	if err != nil {
		return err
	}
	*t = Timestamp(time.Unix(int64(ts), 0))
	return nil
}
Sietse Ringers's avatar
Sietse Ringers committed
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338

// NewServiceProviderJwt returns a new ServiceProviderJwt.
func NewServiceProviderJwt(servername string, dr *DisclosureRequest) *ServiceProviderJwt {
	return &ServiceProviderJwt{
		ServerJwt: ServerJwt{
			ServerName: servername,
			IssuedAt:   Timestamp(time.Now()),
			Type:       "verification_request",
		},
		Request: ServiceProviderRequest{Request: dr},
	}
}

// NewSignatureRequestorJwt returns a new SignatureRequestorJwt.
func NewSignatureRequestorJwt(servername string, sr *SignatureRequest) *SignatureRequestorJwt {
	return &SignatureRequestorJwt{
		ServerJwt: ServerJwt{
			ServerName: servername,
			IssuedAt:   Timestamp(time.Now()),
			Type:       "signature_request",
		},
		Request: SignatureRequestorRequest{Request: sr},
	}
}

// NewIdentityProviderJwt returns a new IdentityProviderJwt.
func NewIdentityProviderJwt(servername string, ir *IssuanceRequest) *IdentityProviderJwt {
	return &IdentityProviderJwt{
		ServerJwt: ServerJwt{
			ServerName: servername,
			IssuedAt:   Timestamp(time.Now()),
			Type:       "issue_request",
		},
		Request: IdentityProviderRequest{Request: ir},
	}
}

// A RequestorJwt contains an IRMA session object.
type RequestorJwt interface {
339
	IrmaSession() IrmaSession
Sietse Ringers's avatar
Sietse Ringers committed
340
341
342
}

// IrmaSession returns an IRMA session object.
343
func (jwt *ServiceProviderJwt) IrmaSession() IrmaSession { return jwt.Request.Request }
Sietse Ringers's avatar
Sietse Ringers committed
344
345

// IrmaSession returns an IRMA session object.
346
func (jwt *SignatureRequestorJwt) IrmaSession() IrmaSession { return jwt.Request.Request }
Sietse Ringers's avatar
Sietse Ringers committed
347
348

// IrmaSession returns an IRMA session object.
349
func (jwt *IdentityProviderJwt) IrmaSession() IrmaSession { return jwt.Request.Request }