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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// 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).
293
294
295
296
297
298
299
300
301
302
303
304
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[:])
}

305
306
307
308
309
// 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))
}

310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
// 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
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364

// 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 {
365
	IrmaSession() IrmaSession
366
	Requestor() string
Sietse Ringers's avatar
Sietse Ringers committed
367
368
}

369
370
func (jwt *ServerJwt) Requestor() string { return jwt.ServerName }

Sietse Ringers's avatar
Sietse Ringers committed
371
// IrmaSession returns an IRMA session object.
372
func (jwt *ServiceProviderJwt) IrmaSession() IrmaSession { return jwt.Request.Request }
Sietse Ringers's avatar
Sietse Ringers committed
373
374

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

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