sessions.go 3.05 KB
Newer Older
1
2
3
4
5
6
package backend

import (
	"math/big"
	"math/rand"
	"sync"
Sietse Ringers's avatar
Sietse Ringers committed
7
	"time"
8
9
10
11
12
13
14
15

	"github.com/go-errors/errors"
	"github.com/mhe/gabi"
	"github.com/privacybydesign/irmago"
	"github.com/privacybydesign/irmago/irmaserver"
)

type session struct {
Sietse Ringers's avatar
Sietse Ringers committed
16
17
	sync.Mutex

18
19
20
21
22
23
	action  irma.Action
	token   string
	version *irma.ProtocolVersion
	request irma.SessionRequest
	status  irmaserver.Status

Sietse Ringers's avatar
Sietse Ringers committed
24
25
	active time.Time

26
27
28
	proofStatus irma.ProofStatus
	disclosed   []*irma.DisclosedAttribute
	signature   *irma.SignedMessage
29
	result      *irmaserver.SessionResult
30
31
32
33
34
35
36

	kssProofs map[irma.SchemeManagerIdentifier]*gabi.ProofP
}

type sessionStore interface {
	get(token string) *session
	add(token string, session *session)
Sietse Ringers's avatar
Sietse Ringers committed
37
	deleteExpired()
38
39
40
41
42
43
44
}

type memorySessionStore struct {
	sync.RWMutex
	m map[string]*session
}

Sietse Ringers's avatar
Sietse Ringers committed
45
46
47
48
49
const (
	maxSessionLifetime = 5 * time.Minute  // After this a session is cancelled
	expiryTicker       = 10 * time.Second // Every so often we check if any session has expired
)

50
51
52
53
54
55
56
57
58
59
60
const sessionChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

var (
	minProtocolVersion = irma.NewVersion(2, 4)
	maxProtocolVersion = irma.NewVersion(2, 4)

	sessions sessionStore = &memorySessionStore{
		m: make(map[string]*session),
	}
)

Sietse Ringers's avatar
Sietse Ringers committed
61
62
63
64
func init() {
	go sessions.deleteExpired()
}

65
66
67
68
69
70
71
72
73
74
75
76
func (s *memorySessionStore) get(token string) *session {
	s.RLock()
	defer s.RUnlock()
	return s.m[token]
}

func (s *memorySessionStore) add(token string, session *session) {
	s.Lock()
	defer s.Unlock()
	s.m[token] = session
}

Sietse Ringers's avatar
Sietse Ringers committed
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
func (s memorySessionStore) deleteExpired() {
	// First check which sessions have expired
	// We don't need a write lock for this yet, so postpone that for actual deleting
	s.RLock()
	expired := make([]string, 0, len(s.m))
	for token, session := range s.m {
		if session.active.Add(5 * time.Minute).Before(time.Now()) {
			conf.Logger.Infof("Session %s expired, deleting", token)
			expired = append(expired, token)
		}
	}
	s.RUnlock()

	// Using a write lock, delete the expired sessions
	s.Lock()
	for _, token := range expired {
		delete(s.m, token)
	}
	s.Unlock()

	// Schedule next run
	time.AfterFunc(expiryTicker, func() {
		s.deleteExpired()
	})
}

103
104
105
106
107
108
109
var one *big.Int = big.NewInt(1)

func newSession(action irma.Action, request irma.SessionRequest) *session {
	s := &session{
		action:  action,
		request: request,
		status:  irmaserver.StatusInitialized,
Sietse Ringers's avatar
Sietse Ringers committed
110
		active:  time.Now(),
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
		token:   newSessionToken(),
	}
	nonce, _ := gabi.RandomBigInt(gabi.DefaultSystemParameters[2048].Lstatzk)
	request.SetNonce(nonce)
	request.SetContext(one)
	sessions.add(s.token, s)
	return s
}

func newSessionToken() string {
	b := make([]byte, 20)
	for i := range b {
		b[i] = sessionChars[rand.Int63()%int64(len(sessionChars))]
	}
	return string(b)
}

func chooseProtocolVersion(min, max *irma.ProtocolVersion) (*irma.ProtocolVersion, error) {
	if min.AboveVersion(minProtocolVersion) || max.BelowVersion(min) {
		return nil, errors.Errorf("Protocol version negotiation failed, min=%s max=%s", min.String(), max.String())
	}
	if max.AboveVersion(maxProtocolVersion) {
		return maxProtocolVersion, nil
	} else {
		return max, nil
	}
}