requests.go 15.4 KB
Newer Older
1
package irma
2
3
4

import (
	"fmt"
5
	"io/ioutil"
6
7
8
9
	"math/big"
	"strconv"
	"time"

10
	"encoding/json"
11
12

	"github.com/bwesterb/go-atum"
Sietse Ringers's avatar
Sietse Ringers committed
13
	"github.com/go-errors/errors"
14
	"github.com/mhe/gabi"
15
	"github.com/privacybydesign/irmago/internal/fs"
Sietse Ringers's avatar
Sietse Ringers committed
16
)
17

18
19
// BaseRequest contains the context and nonce for an IRMA session.
type BaseRequest struct {
20
21
22
	Context *big.Int `json:"context"`
	Nonce   *big.Int `json:"nonce"`
	Type    Action   `json:"type"`
23

24
25
26
	Candidates [][]*AttributeIdentifier `json:"-"`
	Choice     *DisclosureChoice        `json:"-"`
	Ids        *IrmaIdentifierSet       `json:"-"`
27

28
	Version *ProtocolVersion `json:"protocolVersion"`
Sietse Ringers's avatar
Sietse Ringers committed
29
30
}

31
func (sr *BaseRequest) SetCandidates(candidates [][]*AttributeIdentifier) {
32
33
34
	sr.Candidates = candidates
}

35
// DisclosureChoice returns the attributes to be disclosed in this session.
36
func (sr *BaseRequest) DisclosureChoice() *DisclosureChoice {
37
	return sr.Choice
Sietse Ringers's avatar
Sietse Ringers committed
38
39
}

40
// SetDisclosureChoice sets the attributes to be disclosed in this session.
41
func (sr *BaseRequest) SetDisclosureChoice(choice *DisclosureChoice) {
42
	sr.Choice = choice
43
44
}

45
// ...
46
func (sr *BaseRequest) SetVersion(v *ProtocolVersion) {
47
	sr.Version = v
48
49
50
}

// ...
51
func (sr *BaseRequest) GetVersion() *ProtocolVersion {
52
	return sr.Version
53
54
}

Sietse Ringers's avatar
Sietse Ringers committed
55
// A DisclosureRequest is a request to disclose certain attributes.
56
type DisclosureRequest struct {
57
	BaseRequest
58
59
60
	Content AttributeDisjunctionList `json:"content"`
}

Sietse Ringers's avatar
Sietse Ringers committed
61
// A SignatureRequest is a a request to sign a message with certain attributes.
62
63
type SignatureRequest struct {
	DisclosureRequest
64
65
66
	Message string `json:"message"`

	// Session state
67
	Timestamp *atum.Timestamp `json:"-"`
68
69
}

Sietse Ringers's avatar
Sietse Ringers committed
70
71
// An IssuanceRequest is a request to issue certain credentials,
// optionally also asking for certain attributes to be simultaneously disclosed.
72
type IssuanceRequest struct {
73
	BaseRequest
Tomas's avatar
Tomas committed
74
75
76
77
78
	Credentials []*CredentialRequest     `json:"credentials"`
	Disclose    AttributeDisjunctionList `json:"disclose"`

	// Derived data
	CredentialInfoList        CredentialInfoList `json:",omitempty"`
Sietse Ringers's avatar
Sietse Ringers committed
79
	RemovalCredentialInfoList CredentialInfoList
80
81
}

Sietse Ringers's avatar
Sietse Ringers committed
82
83
// A CredentialRequest contains the attributes and metadata of a credential
// that will be issued in an IssuanceRequest.
84
type CredentialRequest struct {
85
86
87
88
	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
89
90
}

Sietse Ringers's avatar
Sietse Ringers committed
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
// 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"`
}

131
132
// SessionRequest is an IRMA session.
type SessionRequest interface {
133
134
135
136
	GetNonce() *big.Int
	SetNonce(*big.Int)
	GetContext() *big.Int
	SetContext(*big.Int)
137
	GetVersion() *ProtocolVersion
138
	SetVersion(*ProtocolVersion)
139
	ToDisclose() AttributeDisjunctionList
140
141
	DisclosureChoice() *DisclosureChoice
	SetDisclosureChoice(choice *DisclosureChoice)
142
	SetCandidates(candidates [][]*AttributeIdentifier)
143
144
145
	Identifiers() *IrmaIdentifierSet
}

Sietse Ringers's avatar
Sietse Ringers committed
146
147
148
// Timestamp is a time.Time that marshals to Unix timestamps.
type Timestamp time.Time

149
150
func (cr *CredentialRequest) Info(conf *Configuration, metadataVersion byte) (*CredentialInfo, error) {
	list, err := cr.AttributeList(conf, metadataVersion)
151
152
153
	if err != nil {
		return nil, err
	}
154
	return NewCredentialInfo(list.Ints, conf), nil
155
156
}

Sietse Ringers's avatar
Sietse Ringers committed
157
// AttributeList returns the list of attributes from this credential request.
158
159
func (cr *CredentialRequest) AttributeList(conf *Configuration, metadataVersion byte) (*AttributeList, error) {
	meta := NewMetadataAttribute(metadataVersion)
Sietse Ringers's avatar
Sietse Ringers committed
160
	meta.setKeyCounter(cr.KeyCounter)
161
	meta.setCredentialTypeIdentifier(cr.CredentialTypeID.String())
Sietse Ringers's avatar
Sietse Ringers committed
162
163
164
165
166
167
	meta.setSigningDate()
	err := meta.setExpiryDate(cr.Validity)
	if err != nil {
		return nil, err
	}

168
	credtype := conf.CredentialTypes[*cr.CredentialTypeID]
Sietse Ringers's avatar
Sietse Ringers committed
169
170
171
	if credtype == nil {
		return nil, errors.New("Unknown credential type")
	}
172
173
174
175
176
177
178
179
180
181
182
183
184
185

	// Check that there are no attributes in the credential request that aren't
	// in the credential descriptor.
	for crName := range cr.Attributes {
		found := false
		for _, ad := range credtype.Attributes {
			if ad.ID == crName {
				found = true
				break
			}
		}
		if !found {
			return nil, errors.New("Unknown CR attribute")
		}
Sietse Ringers's avatar
Sietse Ringers committed
186
187
	}

188
	attrs := make([]*big.Int, len(credtype.Attributes)+1)
Sietse Ringers's avatar
Sietse Ringers committed
189
190
	attrs[0] = meta.Int
	for i, attrtype := range credtype.Attributes {
191
		attrs[i+1] = new(big.Int)
Sietse Ringers's avatar
Sietse Ringers committed
192
		if str, present := cr.Attributes[attrtype.ID]; present {
193
			// Set attribute to str << 1 + 1
194
			attrs[i+1].SetBytes([]byte(str))
195
196
197
198
			if meta.Version() >= 0x03 {
				attrs[i+1].Lsh(attrs[i+1], 1)             // attr <<= 1
				attrs[i+1].Add(attrs[i+1], big.NewInt(1)) // attr += 1
			}
Sietse Ringers's avatar
Sietse Ringers committed
199
		} else {
200
			if attrtype.Optional != "true" {
201
202
				return nil, errors.New("Required attribute not provided")
			}
Sietse Ringers's avatar
Sietse Ringers committed
203
204
205
		}
	}

206
	return NewAttributeListFromInts(attrs, conf), nil
Sietse Ringers's avatar
Sietse Ringers committed
207
208
}

Sietse Ringers's avatar
Sietse Ringers committed
209
func (ir *IssuanceRequest) Identifiers() *IrmaIdentifierSet {
210
211
	if ir.Ids == nil {
		ir.Ids = &IrmaIdentifierSet{
212
213
214
215
			SchemeManagers:  map[SchemeManagerIdentifier]struct{}{},
			Issuers:         map[IssuerIdentifier]struct{}{},
			CredentialTypes: map[CredentialTypeIdentifier]struct{}{},
			PublicKeys:      map[IssuerIdentifier][]int{},
Sietse Ringers's avatar
Sietse Ringers committed
216
217
		}

Sietse Ringers's avatar
Sietse Ringers committed
218
		for _, credreq := range ir.Credentials {
Sietse Ringers's avatar
Sietse Ringers committed
219
			issuer := credreq.CredentialTypeID.IssuerIdentifier()
220
221
222
223
224
			ir.Ids.SchemeManagers[issuer.SchemeManagerIdentifier()] = struct{}{}
			ir.Ids.Issuers[issuer] = struct{}{}
			ir.Ids.CredentialTypes[*credreq.CredentialTypeID] = struct{}{}
			if ir.Ids.PublicKeys[issuer] == nil {
				ir.Ids.PublicKeys[issuer] = []int{}
225
			}
226
			ir.Ids.PublicKeys[issuer] = append(ir.Ids.PublicKeys[issuer], credreq.KeyCounter)
227
228
		}

Sietse Ringers's avatar
Sietse Ringers committed
229
		for _, disjunction := range ir.Disclose {
230
			for _, attr := range disjunction.Attributes {
Sietse Ringers's avatar
Sietse Ringers committed
231
232
233
234
235
236
				var cti CredentialTypeIdentifier
				if !attr.IsCredential() {
					cti = attr.CredentialTypeIdentifier()
				} else {
					cti = NewCredentialTypeIdentifier(attr.String())
				}
237
238
239
				ir.Ids.SchemeManagers[cti.IssuerIdentifier().SchemeManagerIdentifier()] = struct{}{}
				ir.Ids.Issuers[cti.IssuerIdentifier()] = struct{}{}
				ir.Ids.CredentialTypes[cti] = struct{}{}
240
			}
Sietse Ringers's avatar
Sietse Ringers committed
241
242
		}
	}
243
	return ir.Ids
Sietse Ringers's avatar
Sietse Ringers committed
244
245
}

Sietse Ringers's avatar
Sietse Ringers committed
246
// ToDisclose returns the attributes that must be disclosed in this issuance session.
247
248
249
250
251
252
253
func (ir *IssuanceRequest) ToDisclose() AttributeDisjunctionList {
	if ir.Disclose == nil {
		return AttributeDisjunctionList{}
	}

	return ir.Disclose
}
Sietse Ringers's avatar
Sietse Ringers committed
254

Tomas's avatar
Tomas committed
255
256
257
258
259
260
261
262
263
264
265
266
267
func (ir *IssuanceRequest) GetCredentialInfoList(conf *Configuration, version *ProtocolVersion) (CredentialInfoList, error) {
	if ir.CredentialInfoList == nil {
		for _, credreq := range ir.Credentials {
			info, err := credreq.Info(conf, GetMetadataVersion(version))
			if err != nil {
				return nil, err
			}
			ir.CredentialInfoList = append(ir.CredentialInfoList, info)
		}
	}
	return ir.CredentialInfoList, nil
}

Sietse Ringers's avatar
Sietse Ringers committed
268
269
270
271
272
273
274
275
276
277
278
279
// 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 }

280
281
282
283
284
285
286
287
288
289
func (ir *IssuanceRequest) Validate() error {
	if ir.Type != ActionIssuing {
		return errors.New("Not an issuance request")
	}
	if len(ir.Credentials) == 0 {
		return errors.New("Empty issuance request")
	}
	return nil
}

290
func (dr *DisclosureRequest) Identifiers() *IrmaIdentifierSet {
291
292
	if dr.Ids == nil {
		dr.Ids = &IrmaIdentifierSet{
293
294
295
296
			SchemeManagers:  map[SchemeManagerIdentifier]struct{}{},
			Issuers:         map[IssuerIdentifier]struct{}{},
			CredentialTypes: map[CredentialTypeIdentifier]struct{}{},
			PublicKeys:      map[IssuerIdentifier][]int{},
Sietse Ringers's avatar
Sietse Ringers committed
297
		}
298
299
		for _, disjunction := range dr.Content {
			for _, attr := range disjunction.Attributes {
300
301
302
				dr.Ids.SchemeManagers[attr.CredentialTypeIdentifier().IssuerIdentifier().SchemeManagerIdentifier()] = struct{}{}
				dr.Ids.Issuers[attr.CredentialTypeIdentifier().IssuerIdentifier()] = struct{}{}
				dr.Ids.CredentialTypes[attr.CredentialTypeIdentifier()] = struct{}{}
303
			}
Sietse Ringers's avatar
Sietse Ringers committed
304
305
		}
	}
306
	return dr.Ids
Sietse Ringers's avatar
Sietse Ringers committed
307
308
}

Sietse Ringers's avatar
Sietse Ringers committed
309
310
// 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
311
312
313
314
315
316
317
318
319
320
321
322
323

// 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 }

324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
func (dr *DisclosureRequest) Validate() error {
	if dr.Type != ActionDisclosing {
		return errors.New("Not a disclosure request")
	}
	if len(dr.Content) == 0 {
		return errors.New("Disclosure request had no attributes")
	}
	for _, disjunction := range dr.Content {
		if len(disjunction.Attributes) == 0 {
			return errors.New("Disclosure request had an empty disjunction")
		}
	}
	return nil
}

Sietse Ringers's avatar
Sietse Ringers committed
339
340
// GetNonce returns the nonce of this signature session
// (with the message already hashed into it).
341
func (sr *SignatureRequest) GetNonce() *big.Int {
342
	return ASN1ConvertSignatureNonce(sr.Message, sr.Nonce, sr.Timestamp)
343
344
}

345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
// Convert fields in JSON string to BigInterger if they are string
// Supply fieldnames as a slice as second argument
func convertFieldsToBigInt(jsonString []byte, fieldNames []string) ([]byte, error) {
	var rawRequest map[string]interface{}

	err := json.Unmarshal(jsonString, &rawRequest)
	if err != nil {
		return nil, err
	}

	for _, fieldName := range fieldNames {
		field := new(big.Int)
		fieldString := fmt.Sprintf("%v", rawRequest[fieldName])
		field.SetString(fieldString, 10)
		rawRequest[fieldName] = field
	}

	return json.Marshal(rawRequest)
}

// Custom Unmarshalling to support both json with string and int fields for nonce and context
// i.e. {"nonce": "42", "context": "1337", ... } and {"nonce": 42, "context": 1337, ... }
func (sr *SignatureRequest) UnmarshalJSON(b []byte) error {
368
	type signatureRequestTemp SignatureRequest // To avoid 'recursive unmarshalling'
369
370

	fixedRequest, err := convertFieldsToBigInt(b, []string{"nonce", "context"})
Koen van Ingen's avatar
Koen van Ingen committed
371
372
373
	if err != nil {
		return err
	}
374

375
	var result signatureRequestTemp
Koen van Ingen's avatar
Koen van Ingen committed
376
377
378
379
	err = json.Unmarshal(fixedRequest, &result)
	if err != nil {
		return err
	}
380
381
382
383
384
385
386

	sr.DisclosureRequest = result.DisclosureRequest
	sr.Message = result.Message

	return err
}

387
func (sr *SignatureRequest) SignatureFromMessage(message interface{}) (*SignedMessage, error) {
388
389
390
391
392
393
	signature, ok := message.(gabi.ProofList)

	if !ok {
		return nil, errors.Errorf("Type assertion failed")
	}

394
	return &SignedMessage{
395
		Signature: signature,
396
397
398
		Nonce:     sr.Nonce,
		Context:   sr.Context,
		Message:   sr.Message,
399
		Timestamp: sr.Timestamp,
400
401
402
	}, nil
}

403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
func (sr *SignatureRequest) Validate() error {
	if sr.Type != ActionSigning {
		return errors.New("Not a signature request")
	}
	if sr.Message == "" {
		return errors.New("Signature request had empty message")
	}
	if len(sr.Content) == 0 {
		return errors.New("Disclosure request had no attributes")
	}
	for _, disjunction := range sr.Content {
		if len(disjunction.Attributes) == 0 {
			return errors.New("Disclosure request had an empty disjunction")
		}
	}
	return nil
}

421
422
423
424
425
// Check if Timestamp is before other Timestamp. Used for checking expiry of attributes
func (t Timestamp) Before(u Timestamp) bool {
	return time.Time(t).Before(time.Time(u))
}

426
427
428
429
func (t Timestamp) After(u Timestamp) bool {
	return time.Time(t).After(time.Time(u))
}

430
431
// MarshalJSON marshals a timestamp.
func (t *Timestamp) MarshalJSON() ([]byte, error) {
432
	return []byte(t.String()), nil
433
434
435
436
437
438
439
440
441
442
443
}

// 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
444

445
446
447
448
449
// Timestamp implements Stringer.
func (t *Timestamp) String() string {
	return fmt.Sprint(time.Time(*t).Unix())
}

450
451
452
453
454
455
456
457
func readTimestamp(path string) (*Timestamp, bool, error) {
	exists, err := fs.PathExists(path)
	if err != nil {
		return nil, false, err
	}
	if !exists {
		return nil, false, nil
	}
458
459
	bts, err := ioutil.ReadFile(path)
	if err != nil {
460
		return nil, true, errors.New("Could not read scheme manager timestamp")
461
	}
462
463
	ts, err := parseTimestamp(bts)
	return ts, true, err
464
465
}

466
func parseTimestamp(bts []byte) (*Timestamp, error) {
467
468
469
470
471
472
	// Remove final character \n if present
	if bts[len(bts)-1] == '\n' {
		bts = bts[:len(bts)-1]
	}
	// convert from byte slice to string; parse as int
	str, err := strconv.ParseInt(string(bts), 10, 64)
473
	if err != nil {
474
		return nil, err
475
	}
476
477
	ts := Timestamp(time.Unix(str, 0))
	return &ts, nil
478
479
}

Sietse Ringers's avatar
Sietse Ringers committed
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
// 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 {
518
	SessionRequest() SessionRequest
519
	Requestor() string
Sietse Ringers's avatar
Sietse Ringers committed
520
521
}

522
523
func (jwt *ServerJwt) Requestor() string { return jwt.ServerName }

524
525
// SessionRequest returns an IRMA session object.
func (jwt *ServiceProviderJwt) SessionRequest() SessionRequest { return jwt.Request.Request }
Sietse Ringers's avatar
Sietse Ringers committed
526

527
528
// SessionRequest returns an IRMA session object.
func (jwt *SignatureRequestorJwt) SessionRequest() SessionRequest { return jwt.Request.Request }
Sietse Ringers's avatar
Sietse Ringers committed
529

530
531
// SessionRequest returns an IRMA session object.
func (jwt *IdentityProviderJwt) SessionRequest() SessionRequest { return jwt.Request.Request }