requests.go 15.7 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
23
	Context       *big.Int `json:"context"`
	Nonce         *big.Int `json:"nonce"`
	RequestorName string   `json:"requestorName"`
	Type          Action   `json:"type"`
24

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

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

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

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

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

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

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

56
57
58
59
60
61
62
63
func (sr *BaseRequest) GetRequestorName() string {
	return sr.RequestorName
}

func (sr *BaseRequest) SetRequestorName(name string) {
	sr.RequestorName = name
}

Sietse Ringers's avatar
Sietse Ringers committed
64
// A DisclosureRequest is a request to disclose certain attributes.
65
type DisclosureRequest struct {
66
	BaseRequest
67
68
69
	Content AttributeDisjunctionList `json:"content"`
}

Sietse Ringers's avatar
Sietse Ringers committed
70
// A SignatureRequest is a a request to sign a message with certain attributes.
71
72
type SignatureRequest struct {
	DisclosureRequest
73
74
75
	Message string `json:"message"`

	// Session state
76
	Timestamp *atum.Timestamp `json:"-"`
77
78
}

Sietse Ringers's avatar
Sietse Ringers committed
79
80
// An IssuanceRequest is a request to issue certain credentials,
// optionally also asking for certain attributes to be simultaneously disclosed.
81
type IssuanceRequest struct {
82
	BaseRequest
Tomas's avatar
Tomas committed
83
84
85
86
87
	Credentials []*CredentialRequest     `json:"credentials"`
	Disclose    AttributeDisjunctionList `json:"disclose"`

	// Derived data
	CredentialInfoList        CredentialInfoList `json:",omitempty"`
Sietse Ringers's avatar
Sietse Ringers committed
88
	RemovalCredentialInfoList CredentialInfoList
89
90
}

Sietse Ringers's avatar
Sietse Ringers committed
91
92
// A CredentialRequest contains the attributes and metadata of a credential
// that will be issued in an IssuanceRequest.
93
type CredentialRequest struct {
94
95
96
97
	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
98
99
}

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

140
141
// SessionRequest is an IRMA session.
type SessionRequest interface {
142
143
144
145
	GetNonce() *big.Int
	SetNonce(*big.Int)
	GetContext() *big.Int
	SetContext(*big.Int)
146
	GetVersion() *ProtocolVersion
147
	SetVersion(*ProtocolVersion)
148
149
	GetRequestorName() string
	SetRequestorName(string)
150
	ToDisclose() AttributeDisjunctionList
151
152
	DisclosureChoice() *DisclosureChoice
	SetDisclosureChoice(choice *DisclosureChoice)
153
	SetCandidates(candidates [][]*AttributeIdentifier)
154
155
156
	Identifiers() *IrmaIdentifierSet
}

Sietse Ringers's avatar
Sietse Ringers committed
157
158
159
// Timestamp is a time.Time that marshals to Unix timestamps.
type Timestamp time.Time

160
161
func (cr *CredentialRequest) Info(conf *Configuration, metadataVersion byte) (*CredentialInfo, error) {
	list, err := cr.AttributeList(conf, metadataVersion)
162
163
164
	if err != nil {
		return nil, err
	}
165
	return NewCredentialInfo(list.Ints, conf), nil
166
167
}

Sietse Ringers's avatar
Sietse Ringers committed
168
// AttributeList returns the list of attributes from this credential request.
169
170
func (cr *CredentialRequest) AttributeList(conf *Configuration, metadataVersion byte) (*AttributeList, error) {
	meta := NewMetadataAttribute(metadataVersion)
Sietse Ringers's avatar
Sietse Ringers committed
171
	meta.setKeyCounter(cr.KeyCounter)
172
	meta.setCredentialTypeIdentifier(cr.CredentialTypeID.String())
Sietse Ringers's avatar
Sietse Ringers committed
173
174
175
176
177
178
	meta.setSigningDate()
	err := meta.setExpiryDate(cr.Validity)
	if err != nil {
		return nil, err
	}

179
	credtype := conf.CredentialTypes[*cr.CredentialTypeID]
Sietse Ringers's avatar
Sietse Ringers committed
180
181
182
	if credtype == nil {
		return nil, errors.New("Unknown credential type")
	}
183
184
185
186
187
188
189
190
191
192
193
194
195
196

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

199
	attrs := make([]*big.Int, len(credtype.Attributes)+1)
Sietse Ringers's avatar
Sietse Ringers committed
200
201
	attrs[0] = meta.Int
	for i, attrtype := range credtype.Attributes {
202
		attrs[i+1] = new(big.Int)
Sietse Ringers's avatar
Sietse Ringers committed
203
		if str, present := cr.Attributes[attrtype.ID]; present {
204
			// Set attribute to str << 1 + 1
205
			attrs[i+1].SetBytes([]byte(str))
206
207
208
209
			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
210
		} else {
211
			if attrtype.Optional != "true" {
212
213
				return nil, errors.New("Required attribute not provided")
			}
Sietse Ringers's avatar
Sietse Ringers committed
214
215
216
		}
	}

217
	return NewAttributeListFromInts(attrs, conf), nil
Sietse Ringers's avatar
Sietse Ringers committed
218
219
}

Sietse Ringers's avatar
Sietse Ringers committed
220
func (ir *IssuanceRequest) Identifiers() *IrmaIdentifierSet {
221
222
	if ir.Ids == nil {
		ir.Ids = &IrmaIdentifierSet{
223
224
225
226
			SchemeManagers:  map[SchemeManagerIdentifier]struct{}{},
			Issuers:         map[IssuerIdentifier]struct{}{},
			CredentialTypes: map[CredentialTypeIdentifier]struct{}{},
			PublicKeys:      map[IssuerIdentifier][]int{},
Sietse Ringers's avatar
Sietse Ringers committed
227
228
		}

Sietse Ringers's avatar
Sietse Ringers committed
229
		for _, credreq := range ir.Credentials {
Sietse Ringers's avatar
Sietse Ringers committed
230
			issuer := credreq.CredentialTypeID.IssuerIdentifier()
231
232
233
234
235
			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{}
236
			}
237
			ir.Ids.PublicKeys[issuer] = append(ir.Ids.PublicKeys[issuer], credreq.KeyCounter)
238
239
		}

Sietse Ringers's avatar
Sietse Ringers committed
240
		for _, disjunction := range ir.Disclose {
241
			for _, attr := range disjunction.Attributes {
Sietse Ringers's avatar
Sietse Ringers committed
242
243
244
245
246
247
				var cti CredentialTypeIdentifier
				if !attr.IsCredential() {
					cti = attr.CredentialTypeIdentifier()
				} else {
					cti = NewCredentialTypeIdentifier(attr.String())
				}
248
249
250
				ir.Ids.SchemeManagers[cti.IssuerIdentifier().SchemeManagerIdentifier()] = struct{}{}
				ir.Ids.Issuers[cti.IssuerIdentifier()] = struct{}{}
				ir.Ids.CredentialTypes[cti] = struct{}{}
251
			}
Sietse Ringers's avatar
Sietse Ringers committed
252
253
		}
	}
254
	return ir.Ids
Sietse Ringers's avatar
Sietse Ringers committed
255
256
}

Sietse Ringers's avatar
Sietse Ringers committed
257
// ToDisclose returns the attributes that must be disclosed in this issuance session.
258
259
260
261
262
263
264
func (ir *IssuanceRequest) ToDisclose() AttributeDisjunctionList {
	if ir.Disclose == nil {
		return AttributeDisjunctionList{}
	}

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

Tomas's avatar
Tomas committed
266
267
268
269
270
271
272
273
274
275
276
277
278
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
279
280
281
282
283
284
285
286
287
288
289
290
// 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 }

291
292
293
294
295
296
297
298
299
300
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
}

301
func (dr *DisclosureRequest) Identifiers() *IrmaIdentifierSet {
302
303
	if dr.Ids == nil {
		dr.Ids = &IrmaIdentifierSet{
304
305
306
307
			SchemeManagers:  map[SchemeManagerIdentifier]struct{}{},
			Issuers:         map[IssuerIdentifier]struct{}{},
			CredentialTypes: map[CredentialTypeIdentifier]struct{}{},
			PublicKeys:      map[IssuerIdentifier][]int{},
Sietse Ringers's avatar
Sietse Ringers committed
308
		}
309
310
		for _, disjunction := range dr.Content {
			for _, attr := range disjunction.Attributes {
311
312
313
				dr.Ids.SchemeManagers[attr.CredentialTypeIdentifier().IssuerIdentifier().SchemeManagerIdentifier()] = struct{}{}
				dr.Ids.Issuers[attr.CredentialTypeIdentifier().IssuerIdentifier()] = struct{}{}
				dr.Ids.CredentialTypes[attr.CredentialTypeIdentifier()] = struct{}{}
314
			}
Sietse Ringers's avatar
Sietse Ringers committed
315
316
		}
	}
317
	return dr.Ids
Sietse Ringers's avatar
Sietse Ringers committed
318
319
}

Sietse Ringers's avatar
Sietse Ringers committed
320
321
// 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
322
323
324
325
326
327
328
329
330
331
332
333
334

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

335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
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
350
351
// GetNonce returns the nonce of this signature session
// (with the message already hashed into it).
352
func (sr *SignatureRequest) GetNonce() *big.Int {
353
	return ASN1ConvertSignatureNonce(sr.Message, sr.Nonce, sr.Timestamp)
354
355
}

356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
// 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 {
379
	type signatureRequestTemp SignatureRequest // To avoid 'recursive unmarshalling'
380
381

	fixedRequest, err := convertFieldsToBigInt(b, []string{"nonce", "context"})
Koen van Ingen's avatar
Koen van Ingen committed
382
383
384
	if err != nil {
		return err
	}
385

386
	var result signatureRequestTemp
Koen van Ingen's avatar
Koen van Ingen committed
387
388
389
390
	err = json.Unmarshal(fixedRequest, &result)
	if err != nil {
		return err
	}
391
392
393
394
395
396
397

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

	return err
}

398
func (sr *SignatureRequest) SignatureFromMessage(message interface{}) (*SignedMessage, error) {
399
400
401
402
403
404
	signature, ok := message.(gabi.ProofList)

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

405
	return &SignedMessage{
406
		Signature: signature,
407
408
409
		Nonce:     sr.Nonce,
		Context:   sr.Context,
		Message:   sr.Message,
410
		Timestamp: sr.Timestamp,
411
412
413
	}, nil
}

414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
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
}

432
433
434
435
436
// 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))
}

437
438
439
440
func (t Timestamp) After(u Timestamp) bool {
	return time.Time(t).After(time.Time(u))
}

441
442
// MarshalJSON marshals a timestamp.
func (t *Timestamp) MarshalJSON() ([]byte, error) {
443
	return []byte(t.String()), nil
444
445
446
447
448
449
450
451
452
453
454
}

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

456
457
458
459
460
// Timestamp implements Stringer.
func (t *Timestamp) String() string {
	return fmt.Sprint(time.Time(*t).Unix())
}

461
462
463
464
465
466
467
468
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
	}
469
470
	bts, err := ioutil.ReadFile(path)
	if err != nil {
471
		return nil, true, errors.New("Could not read scheme manager timestamp")
472
	}
473
474
	ts, err := parseTimestamp(bts)
	return ts, true, err
475
476
}

477
func parseTimestamp(bts []byte) (*Timestamp, error) {
478
479
480
481
482
483
	// 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)
484
	if err != nil {
485
		return nil, err
486
	}
487
488
	ts := Timestamp(time.Unix(str, 0))
	return &ts, nil
489
490
}

Sietse Ringers's avatar
Sietse Ringers committed
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
518
519
520
521
522
523
524
525
526
527
528
// 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 {
529
	SessionRequest() SessionRequest
530
	Requestor() string
Sietse Ringers's avatar
Sietse Ringers committed
531
532
}

533
534
func (jwt *ServerJwt) Requestor() string { return jwt.ServerName }

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

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

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