requests.go 12.9 KB
Newer Older
1
package irma
2
3
4
5
6
7
8

import (
	"fmt"
	"math/big"
	"strconv"
	"time"

9
	"encoding/json"
10
11

	"github.com/bwesterb/go-atum"
Sietse Ringers's avatar
Sietse Ringers committed
12
	"github.com/go-errors/errors"
13
	"github.com/mhe/gabi"
Sietse Ringers's avatar
Sietse Ringers committed
14
)
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
20
	Context    *big.Int                 `json:"context"`
	Nonce      *big.Int                 `json:"nonce"`
	Candidates [][]*AttributeIdentifier `json:"-"`
21
22
23

	Choice *DisclosureChoice  `json:"-"`
	Ids    *IrmaIdentifierSet `json:"-"`
24
25

	version *ProtocolVersion
Sietse Ringers's avatar
Sietse Ringers committed
26
27
}

28
29
30
31
func (sr *SessionRequest) SetCandidates(candidates [][]*AttributeIdentifier) {
	sr.Candidates = candidates
}

32
// DisclosureChoice returns the attributes to be disclosed in this session.
Sietse Ringers's avatar
Sietse Ringers committed
33
func (sr *SessionRequest) DisclosureChoice() *DisclosureChoice {
34
	return sr.Choice
Sietse Ringers's avatar
Sietse Ringers committed
35
36
}

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

42
43
44
45
46
47
48
49
50
51
// ...
func (sr *SessionRequest) SetVersion(v *ProtocolVersion) {
	sr.version = v
}

// ...
func (sr *SessionRequest) GetVersion() *ProtocolVersion {
	return sr.version
}

Sietse Ringers's avatar
Sietse Ringers committed
52
// A DisclosureRequest is a request to disclose certain attributes.
53
54
55
56
57
type DisclosureRequest struct {
	SessionRequest
	Content AttributeDisjunctionList `json:"content"`
}

Sietse Ringers's avatar
Sietse Ringers committed
58
// A SignatureRequest is a a request to sign a message with certain attributes.
59
60
type SignatureRequest struct {
	DisclosureRequest
61
62
	Message   string          `json:"message"`
	Timestamp *atum.Timestamp `json:"-"`
63
64
}

Sietse Ringers's avatar
Sietse Ringers committed
65
66
// An IssuanceRequest is a request to issue certain credentials,
// optionally also asking for certain attributes to be simultaneously disclosed.
67
68
type IssuanceRequest struct {
	SessionRequest
69
70
71
	Credentials        []*CredentialRequest     `json:"credentials"`
	Disclose           AttributeDisjunctionList `json:"disclose"`
	CredentialInfoList CredentialInfoList       `json:",omitempty"`
72
73
}

Sietse Ringers's avatar
Sietse Ringers committed
74
75
// A CredentialRequest contains the attributes and metadata of a credential
// that will be issued in an IssuanceRequest.
76
type CredentialRequest struct {
77
78
79
80
	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
81
82
}

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

123
124
125
126
127
128
// IrmaSession is an IRMA session.
type IrmaSession interface {
	GetNonce() *big.Int
	SetNonce(*big.Int)
	GetContext() *big.Int
	SetContext(*big.Int)
129
	SetVersion(*ProtocolVersion)
130
	ToDisclose() AttributeDisjunctionList
131
132
	DisclosureChoice() *DisclosureChoice
	SetDisclosureChoice(choice *DisclosureChoice)
133
	SetCandidates(candidates [][]*AttributeIdentifier)
134
135
136
	Identifiers() *IrmaIdentifierSet
}

Sietse Ringers's avatar
Sietse Ringers committed
137
138
139
// Timestamp is a time.Time that marshals to Unix timestamps.
type Timestamp time.Time

140
141
func (cr *CredentialRequest) Info(conf *Configuration, metadataVersion byte) (*CredentialInfo, error) {
	list, err := cr.AttributeList(conf, metadataVersion)
142
143
144
	if err != nil {
		return nil, err
	}
145
	return NewCredentialInfo(list.Ints, conf), nil
146
147
}

Sietse Ringers's avatar
Sietse Ringers committed
148
// AttributeList returns the list of attributes from this credential request.
149
150
func (cr *CredentialRequest) AttributeList(conf *Configuration, metadataVersion byte) (*AttributeList, error) {
	meta := NewMetadataAttribute(metadataVersion)
Sietse Ringers's avatar
Sietse Ringers committed
151
	meta.setKeyCounter(cr.KeyCounter)
152
	meta.setCredentialTypeIdentifier(cr.CredentialTypeID.String())
Sietse Ringers's avatar
Sietse Ringers committed
153
154
155
156
157
158
	meta.setSigningDate()
	err := meta.setExpiryDate(cr.Validity)
	if err != nil {
		return nil, err
	}

159
	credtype := conf.CredentialTypes[*cr.CredentialTypeID]
Sietse Ringers's avatar
Sietse Ringers committed
160
161
162
	if credtype == nil {
		return nil, errors.New("Unknown credential type")
	}
163
164
165
166
167
168
169
170
171
172
173
174
175
176

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

179
	attrs := make([]*big.Int, len(credtype.Attributes)+1)
Sietse Ringers's avatar
Sietse Ringers committed
180
181
	attrs[0] = meta.Int
	for i, attrtype := range credtype.Attributes {
182
		attrs[i+1] = new(big.Int)
Sietse Ringers's avatar
Sietse Ringers committed
183
		if str, present := cr.Attributes[attrtype.ID]; present {
184
			// Set attribute to str << 1 + 1
185
			attrs[i+1].SetBytes([]byte(str))
186
187
188
189
			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
190
		} else {
191
			if attrtype.Optional != "true" {
192
193
				return nil, errors.New("Required attribute not provided")
			}
Sietse Ringers's avatar
Sietse Ringers committed
194
195
196
		}
	}

197
	return NewAttributeListFromInts(attrs, conf), nil
Sietse Ringers's avatar
Sietse Ringers committed
198
199
}

Sietse Ringers's avatar
Sietse Ringers committed
200
func (ir *IssuanceRequest) Identifiers() *IrmaIdentifierSet {
201
202
	if ir.Ids == nil {
		ir.Ids = &IrmaIdentifierSet{
203
204
205
206
			SchemeManagers:  map[SchemeManagerIdentifier]struct{}{},
			Issuers:         map[IssuerIdentifier]struct{}{},
			CredentialTypes: map[CredentialTypeIdentifier]struct{}{},
			PublicKeys:      map[IssuerIdentifier][]int{},
Sietse Ringers's avatar
Sietse Ringers committed
207
208
		}

Sietse Ringers's avatar
Sietse Ringers committed
209
		for _, credreq := range ir.Credentials {
Sietse Ringers's avatar
Sietse Ringers committed
210
			issuer := credreq.CredentialTypeID.IssuerIdentifier()
211
212
213
214
215
			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{}
216
			}
217
			ir.Ids.PublicKeys[issuer] = append(ir.Ids.PublicKeys[issuer], credreq.KeyCounter)
218
219
		}

Sietse Ringers's avatar
Sietse Ringers committed
220
		for _, disjunction := range ir.Disclose {
221
			for _, attr := range disjunction.Attributes {
Sietse Ringers's avatar
Sietse Ringers committed
222
223
224
225
226
227
				var cti CredentialTypeIdentifier
				if !attr.IsCredential() {
					cti = attr.CredentialTypeIdentifier()
				} else {
					cti = NewCredentialTypeIdentifier(attr.String())
				}
228
229
230
				ir.Ids.SchemeManagers[cti.IssuerIdentifier().SchemeManagerIdentifier()] = struct{}{}
				ir.Ids.Issuers[cti.IssuerIdentifier()] = struct{}{}
				ir.Ids.CredentialTypes[cti] = struct{}{}
231
			}
Sietse Ringers's avatar
Sietse Ringers committed
232
233
		}
	}
234
	return ir.Ids
Sietse Ringers's avatar
Sietse Ringers committed
235
236
}

Sietse Ringers's avatar
Sietse Ringers committed
237
// ToDisclose returns the attributes that must be disclosed in this issuance session.
238
239
240
241
242
243
244
func (ir *IssuanceRequest) ToDisclose() AttributeDisjunctionList {
	if ir.Disclose == nil {
		return AttributeDisjunctionList{}
	}

	return ir.Disclose
}
Sietse Ringers's avatar
Sietse Ringers committed
245
246
247
248
249
250
251
252
253
254
255
256
257

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

258
func (dr *DisclosureRequest) Identifiers() *IrmaIdentifierSet {
259
260
	if dr.Ids == nil {
		dr.Ids = &IrmaIdentifierSet{
261
262
263
264
			SchemeManagers:  map[SchemeManagerIdentifier]struct{}{},
			Issuers:         map[IssuerIdentifier]struct{}{},
			CredentialTypes: map[CredentialTypeIdentifier]struct{}{},
			PublicKeys:      map[IssuerIdentifier][]int{},
Sietse Ringers's avatar
Sietse Ringers committed
265
		}
266
267
		for _, disjunction := range dr.Content {
			for _, attr := range disjunction.Attributes {
268
269
270
				dr.Ids.SchemeManagers[attr.CredentialTypeIdentifier().IssuerIdentifier().SchemeManagerIdentifier()] = struct{}{}
				dr.Ids.Issuers[attr.CredentialTypeIdentifier().IssuerIdentifier()] = struct{}{}
				dr.Ids.CredentialTypes[attr.CredentialTypeIdentifier()] = struct{}{}
271
			}
Sietse Ringers's avatar
Sietse Ringers committed
272
273
		}
	}
274
	return dr.Ids
Sietse Ringers's avatar
Sietse Ringers committed
275
276
}

Sietse Ringers's avatar
Sietse Ringers committed
277
278
// 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
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293

// 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).
294
func (sr *SignatureRequest) GetNonce() *big.Int {
295
	return ASN1ConvertSignatureNonce(sr.Message, sr.Nonce, sr.Timestamp)
296
297
}

298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
// 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 {
	type SignatureRequestTemp SignatureRequest // To avoid 'recursive unmarshalling'

	fixedRequest, err := convertFieldsToBigInt(b, []string{"nonce", "context"})
Koen van Ingen's avatar
Koen van Ingen committed
324
325
326
	if err != nil {
		return err
	}
327
328

	var result SignatureRequestTemp
Koen van Ingen's avatar
Koen van Ingen committed
329
330
331
332
	err = json.Unmarshal(fixedRequest, &result)
	if err != nil {
		return err
	}
333
334
335
336
337
338
339

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

	return err
}

340
341
342
343
344
345
346
347
func (sr *SignatureRequest) SignatureFromMessage(message interface{}) (*IrmaSignedMessage, error) {
	signature, ok := message.(gabi.ProofList)

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

	return &IrmaSignedMessage{
348
		Signature: signature,
349
350
351
		Nonce:     sr.Nonce,
		Context:   sr.Context,
		Message:   sr.Message,
352
		Timestamp: sr.Timestamp,
353
354
355
	}, nil
}

356
357
358
359
360
// 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))
}

361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
// 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
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415

// 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 {
416
	IrmaSession() IrmaSession
417
	Requestor() string
Sietse Ringers's avatar
Sietse Ringers committed
418
419
}

420
421
func (jwt *ServerJwt) Requestor() string { return jwt.ServerName }

Sietse Ringers's avatar
Sietse Ringers committed
422
// IrmaSession returns an IRMA session object.
423
func (jwt *ServiceProviderJwt) IrmaSession() IrmaSession { return jwt.Request.Request }
Sietse Ringers's avatar
Sietse Ringers committed
424
425

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

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