messages.go 4.21 KB
Newer Older
Sietse Ringers's avatar
Sietse Ringers committed
1
2
3
4
5
6
package irmago

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
Sietse Ringers's avatar
Sietse Ringers committed
7
	"math/big"
Sietse Ringers's avatar
Sietse Ringers committed
8
	"strings"
Sietse Ringers's avatar
Sietse Ringers committed
9
10

	"github.com/go-errors/errors"
Sietse Ringers's avatar
Sietse Ringers committed
11
12
)

Sietse Ringers's avatar
Sietse Ringers committed
13
14
15
16
17
18
// Status encodes the status of an IRMA session (e.g., connected).
type Status string

// Version encodes the IRMA protocol version of an IRMA session.
type Version string

Sietse Ringers's avatar
Sietse Ringers committed
19
20
21
// Action encodes the session type of an IRMA session (e.g., disclosing).
type Action string

Tomas's avatar
Tomas committed
22
23
// ErrorType are session errors.
type ErrorType string
Sietse Ringers's avatar
Sietse Ringers committed
24

Tomas's avatar
Tomas committed
25
26
// SessionError is a protocol error.
type SessionError struct {
Sietse Ringers's avatar
Sietse Ringers committed
27
	Err error
Tomas's avatar
Tomas committed
28
	ErrorType
Sietse Ringers's avatar
Sietse Ringers committed
29
30
31
32
33
34
35
36
37
38
39
40
41
42
	*ApiError
	Info   string
	Status int
}

// ApiError is an error message returned by the API server on errors.
type ApiError struct {
	Status      int    `json:"status"`
	ErrorName   string `json:"error"`
	Description string `json:"description"`
	Message     string `json:"message"`
	Stacktrace  string `json:"stacktrace"`
}

Sietse Ringers's avatar
Sietse Ringers committed
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// Qr contains the data of an IRMA session QR (as generated by irma_js),
// suitable for NewSession().
type Qr struct {
	// Server with which to perform the session
	URL string `json:"u"`
	// Session type (disclosing, signing, issuing)
	Type               Action `json:"irmaqr"`
	ProtocolVersion    string `json:"v"`
	ProtocolMaxVersion string `json:"vmax"`
}

// A SessionInfo is the first message in the IRMA protocol (i.e., GET on the server URL),
// containing the session request info.
type SessionInfo struct {
	Jwt     string                   `json:"jwt"`
	Nonce   *big.Int                 `json:"nonce"`
	Context *big.Int                 `json:"context"`
	Keys    map[IssuerIdentifier]int `json:"keys"`
}

// Statuses
const (
	StatusConnected     = Status("connected")
	StatusCommunicating = Status("communicating")
)

Sietse Ringers's avatar
Sietse Ringers committed
69
70
// Actions
const (
71
72
73
74
75
	ActionSchemeManager = Action("schememanager")
	ActionDisclosing    = Action("disclosing")
	ActionSigning       = Action("signing")
	ActionIssuing       = Action("issuing")
	ActionUnknown       = Action("unknown")
Sietse Ringers's avatar
Sietse Ringers committed
76
77
78
79
80
)

// Protocol errors
const (
	// Protocol version not supported
Tomas's avatar
Tomas committed
81
	ErrorProtocolVersionNotSupported = ErrorType("protocolVersionNotSupported")
Sietse Ringers's avatar
Sietse Ringers committed
82
	// Error in HTTP communication
Tomas's avatar
Tomas committed
83
	ErrorTransport = ErrorType("transport")
Sietse Ringers's avatar
Sietse Ringers committed
84
	// Invalid client JWT in first IRMA message
Tomas's avatar
Tomas committed
85
	ErrorInvalidJWT = ErrorType("invalidJwt")
Sietse Ringers's avatar
Sietse Ringers committed
86
	// Unkown session type (not disclosing, signing, or issuing)
Tomas's avatar
Tomas committed
87
	ErrorUnknownAction = ErrorType("unknownAction")
Sietse Ringers's avatar
Sietse Ringers committed
88
	// Crypto error during calculation of our response (second IRMA message)
Tomas's avatar
Tomas committed
89
	ErrorCrypto = ErrorType("crypto")
Sietse Ringers's avatar
Sietse Ringers committed
90
	// Server rejected our response (second IRMA message)
Tomas's avatar
Tomas committed
91
	ErrorRejected = ErrorType("rejected")
Sietse Ringers's avatar
Sietse Ringers committed
92
	// (De)serializing of a message failed
Tomas's avatar
Tomas committed
93
	ErrorSerialization = ErrorType("serialization")
Sietse Ringers's avatar
Sietse Ringers committed
94
	// Error in keyshare protocol
Tomas's avatar
Tomas committed
95
	ErrorKeyshare = ErrorType("keyshare")
Sietse Ringers's avatar
Sietse Ringers committed
96
	// Keyshare server has blocked us
Tomas's avatar
Tomas committed
97
	ErrorKeyshareBlocked = ErrorType("keyshareBlocked")
98
	// API server error
Tomas's avatar
Tomas committed
99
	ErrorApi = ErrorType("api")
100
	// Server returned unexpected or malformed response
Tomas's avatar
Tomas committed
101
	ErrorServerResponse = ErrorType("serverResponse")
102
103
	// Error during downloading of credential type, issuer, or public keys
	ErrorConfigurationStoreDownload = ErrorType("configurationStoreDownload")
Sietse Ringers's avatar
Sietse Ringers committed
104
105
)

Tomas's avatar
Tomas committed
106
func (e *SessionError) Error() string {
Sietse Ringers's avatar
Sietse Ringers committed
107
	if e.Err != nil {
Tomas's avatar
Tomas committed
108
		return fmt.Sprintf("%s: %s", string(e.ErrorType), e.Err.Error())
Sietse Ringers's avatar
Sietse Ringers committed
109
	}
Tomas's avatar
Tomas committed
110
	return string(e.ErrorType)
Sietse Ringers's avatar
Sietse Ringers committed
111
112
}

Sietse Ringers's avatar
Sietse Ringers committed
113
func jwtDecode(jwt string, body interface{}) (string, error) {
Sietse Ringers's avatar
Sietse Ringers committed
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
	jwtparts := strings.Split(jwt, ".")
	if jwtparts == nil || len(jwtparts) < 2 {
		return "", errors.New("Not a JWT")
	}
	headerbytes, err := base64.RawStdEncoding.DecodeString(jwtparts[0])
	if err != nil {
		return "", err
	}
	var header struct {
		Issuer string `json:"iss"`
	}
	err = json.Unmarshal([]byte(headerbytes), &header)
	if err != nil {
		return "", err
	}

	bodybytes, err := base64.RawStdEncoding.DecodeString(jwtparts[1])
	if err != nil {
		return "", err
	}
	return header.Issuer, json.Unmarshal(bodybytes, body)
}
Sietse Ringers's avatar
Sietse Ringers committed
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154

func parseRequestorJwt(action Action, jwt string) (RequestorJwt, string, error) {
	var retval RequestorJwt
	switch action {
	case ActionDisclosing:
		retval = &ServiceProviderJwt{}
	case ActionSigning:
		retval = &SignatureRequestorJwt{}
	case ActionIssuing:
		retval = &IdentityProviderJwt{}
	default:
		return nil, "", errors.New("Invalid session type")
	}
	server, err := jwtDecode(jwt, retval)
	if err != nil {
		return nil, "", err
	}
	return retval, server, nil
}