Commit a9c3094e authored by Sietse Ringers's avatar Sietse Ringers
Browse files

Merge branch 'master' into newkeyshareserver-edits

parents 7e593648 f00b9f8d
......@@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.8.0] - 2021-03-17
### Added
* Support for device pairing to prevent shoulder surfing (i.e. make it impossible for someone in close physical proximity to a user to scan the QR code that was meant for the user)
* Introduced new frontend endpoints to manage device pairing
* The API of the `irmaserver` package has two new functions `SetFrontendOptions` and `PairingCompleted`
* A new server status `"PAIRING"` is introduced
* A new function `SessionStatus` is available in the API of the `irmaserver` to get a channel with status updates of an IRMA session
### Changes
* The `server.SessionPackage` struct now contains an extra field `FrontendAuth`
* The `irma.Qr` struct now contains an optional field `PairingRecommended` (named `pairingHint` when being marshalled to JSON) that is set to true when pairing is recommended for that session, as indication to the frontend
* The `StartSession` function from the API of the `irmaserver` package now returns three values: the session pointer (type *irma.QR), the requestor token (type irma.RequestorToken) and the frontend authorization token (type irma.FrontendAuthorization)
* The `token` parameter, as used by most functions in the API of the `irmaserver` package, now has the type `irma.RequestorToken`
* The `server.Status` type has been moved to `irma.ServerStatus`; the related constants are also moved, e.g. from `server.StatusInitialized` to `irma.ServerStatusInitialized`
## [0.7.0] - 2021-03-17
### Fixed
* Bug causing scheme updating to fail if OS temp dir is on other file system than the schemes
......@@ -155,6 +170,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Combined issuance-disclosure requests with two schemes one of which has a keyshare server now work as expected
- Various other bugfixes
[0.8.0]: https://github.com/privacybydesign/irmago/compare/v0.7.0...v0.8.0
[0.7.0]: https://github.com/privacybydesign/irmago/compare/v0.6.1...v0.7.0
[0.6.1]: https://github.com/privacybydesign/irmago/compare/v0.6.0...v0.6.1
[0.6.0]: https://github.com/privacybydesign/irmago/compare/v0.5.1...v0.6.0
......
......@@ -4,6 +4,7 @@ import (
"encoding/xml"
"fmt"
"path/filepath"
"regexp"
"github.com/go-errors/errors"
"github.com/privacybydesign/irmago/internal/common"
......@@ -149,6 +150,8 @@ type (
Title TranslatedString `json:"title"`
Logo *string `json:"logo,omitempty"` // SHA256 of the logo contents (which is the filename on disk)
LogoPath *string `json:"logoPath,omitempty"` // Full path to the logo set automatically during scheme parsing
Color *string `json:"color,omitempty"`
TextColor *string `json:"textColor,omitempty"`
Issues *CredentialTypeIdentifier `json:"issues,omitempty"`
AllowOtherRequestors bool `json:"allowOtherRequestors"`
......@@ -356,6 +359,14 @@ func (wizard *IssueWizard) Validate(conf *Configuration) error {
if (wizard.SuccessHeader == nil) != (wizard.SuccessText == nil) {
return errors.New("wizard contents must have success header and text either both specified, or both empty")
}
if wizard.Color != nil && !regexp.MustCompile("^#[0-9A-F]{6}$").MatchString(*wizard.Color) {
return errors.New("invalid wizard color: must be of the form #RRGGBB")
}
if wizard.TextColor != nil && !regexp.MustCompile("^#[0-9A-F]{6}$").MatchString(*wizard.TextColor) {
return errors.New("invalid wizard text color: must be of the form #RRGGBB")
}
// validate that no possible content graph is too complex
allRelevantPaths := wizard.Contents.buildValidationPaths(conf, map[CredentialTypeIdentifier]struct{}{})
for _, contents := range allRelevantPaths {
......
......@@ -20,6 +20,7 @@ require (
github.com/hashicorp/go-retryablehttp v0.6.2
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect
github.com/jackc/pgx v3.6.2+incompatible
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jasonlvhit/gocron v0.0.0-20180312192515-54194c9749d4
github.com/jinzhu/gorm v1.9.12
github.com/lib/pq v1.3.0 // indirect
......@@ -31,6 +32,7 @@ require (
github.com/onsi/gomega v1.9.0 // indirect
github.com/privacybydesign/gabi v0.0.0-20210520084001-626ad7db13a6
github.com/shopspring/decimal v1.2.0 // indirect
github.com/pelletier/go-toml v1.2.0 // indirect
github.com/sietseringers/cobra v1.0.1-0.20200909200314-c50c3838234b
github.com/sietseringers/go-sse v0.0.0-20200801161811-e2cf2c63ca50
github.com/sietseringers/pflag v1.0.4-0.20200909193609-0cde7e893819
......
......@@ -182,6 +182,7 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
......@@ -281,8 +282,13 @@ github.com/spf13/afero v1.2.0 h1:O9FblXGxoTc51M+cqr74Bm2Tmt4PvkA5iu/j8HrkNuY=
github.com/spf13/afero v1.2.0/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
......@@ -299,6 +305,7 @@ github.com/timshannon/bolthold v0.0.0-20180829183128-83840edea944/go.mod h1:jUig
github.com/timshannon/bolthold v0.0.0-20190812165541-a85bcc049a2e h1:FC5JjwU5y5ZBR/vH8LhmPman3k5dep45jRyCpR1VDVQ=
github.com/timshannon/bolthold v0.0.0-20190812165541-a85bcc049a2e/go.mod h1:jUigdmrbdCxcIDEFrq82t4X9805XZfwFZoYUap0ET/U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
......@@ -306,6 +313,7 @@ github.com/x448/float16 v0.8.3/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcY
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
go.etcd.io/bbolt v1.3.0/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
......@@ -357,6 +365,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
......@@ -432,6 +441,7 @@ google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBr
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
......@@ -447,6 +457,7 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
......
......@@ -28,6 +28,8 @@ var ForceHTTPS = true
const (
sessionChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
sessionTokenLength = 20
pairingCodeChars = "0123456789"
pairingCodeLength = 4
)
// AssertPathExists returns nil only if it has been successfully
......@@ -274,15 +276,23 @@ type SSECtx struct {
}
func NewSessionToken() string {
r := make([]byte, sessionTokenLength)
return newRandomString(sessionTokenLength, sessionChars)
}
func NewPairingCode() string {
return newRandomString(pairingCodeLength, pairingCodeChars)
}
func newRandomString(count int, characterSet string) string {
r := make([]byte, count)
_, err := rand.Read(r)
if err != nil {
panic(err)
}
b := make([]byte, sessionTokenLength)
b := make([]byte, count)
for i := range b {
b[i] = sessionChars[r[i]%byte(len(sessionChars))]
b[i] = characterSet[r[i]%byte(len(characterSet))]
}
return string(b)
}
......
......@@ -82,6 +82,9 @@ type TestHandler struct {
expectedServerName *irma.RequestorInfo
wait time.Duration
result string
pairingCodeChan chan string
dismisser irmaclient.SessionDismisser
frontendTransport *irma.HTTPTransport
}
func (th TestHandler) KeyshareEnrollmentIncomplete(manager irma.SchemeManagerIdentifier) {
......@@ -96,7 +99,7 @@ func (th TestHandler) KeyshareEnrollmentMissing(manager irma.SchemeManagerIdenti
func (th TestHandler) KeyshareEnrollmentDeleted(manager irma.SchemeManagerIdentifier) {
th.Failure(&irma.SessionError{Err: errors.Errorf("Keyshare enrollment deleted for %s", manager.String())})
}
func (th TestHandler) StatusUpdate(action irma.Action, status irma.Status) {}
func (th TestHandler) StatusUpdate(action irma.Action, status irma.ClientStatus) {}
func (th *TestHandler) Success(result string) {
th.result = result
th.c <- nil
......@@ -150,6 +153,15 @@ func (th TestHandler) RequestSchemeManagerPermission(manager *irma.SchemeManager
func (th TestHandler) RequestPin(remainingAttempts int, callback irmaclient.PinHandler) {
callback(true, "12345")
}
func (th TestHandler) PairingRequired(pairingCode string) {
// Send pairing code via channel to calling test. This is done such that
// calling tests can detect it when this handler is skipped unexpectedly.
if th.pairingCodeChan != nil {
th.pairingCodeChan <- pairingCode
return
}
th.Failure(&irma.SessionError{ErrorType: irma.ErrorType("Pairing required")})
}
type SessionResult struct {
Err error
......@@ -197,7 +209,7 @@ type ManualTestHandler struct {
action irma.Action
}
func (th *ManualTestHandler) StatusUpdate(action irma.Action, status irma.Status) {
func (th *ManualTestHandler) StatusUpdate(action irma.Action, status irma.ClientStatus) {
th.action = action
}
......
......@@ -34,3 +34,39 @@ func TestSessionUsingLegacyStorage(t *testing.T) {
// Test whether credential is still there after the storage has been reloaded
sessionHelper(t, getDisclosureRequest(idRoot), "verification", client)
}
func TestWithoutPairingSupport(t *testing.T) {
defaultMaxVersion := maxClientVersion
defer func() {
maxClientVersion = defaultMaxVersion
}()
maxClientVersion = &irma.ProtocolVersion{Major: 2, Minor: 7}
t.Run("TestSigningSession", TestSigningSession)
t.Run("TestDisclosureSession", TestDisclosureSession)
t.Run("TestNoAttributeDisclosureSession", TestNoAttributeDisclosureSession)
t.Run("TestEmptyDisclosure", TestEmptyDisclosure)
t.Run("TestIssuanceSession", TestIssuanceSession)
t.Run("TestMultipleIssuanceSession", TestMultipleIssuanceSession)
t.Run("TestDefaultCredentialValidity", TestDefaultCredentialValidity)
t.Run("TestIssuanceDisclosureEmptyAttributes", TestIssuanceDisclosureEmptyAttributes)
t.Run("TestIssuanceOptionalZeroLengthAttributes", TestIssuanceOptionalZeroLengthAttributes)
t.Run("TestIssuanceOptionalSetAttributes", TestIssuanceOptionalSetAttributes)
t.Run("TestIssuanceSameAttributesNotSingleton", TestIssuanceSameAttributesNotSingleton)
t.Run("TestIssuancePairing", TestIssuancePairing)
t.Run("TestLargeAttribute", TestLargeAttribute)
t.Run("TestIssuanceSingletonCredential", TestIssuanceSingletonCredential)
t.Run("TestUnsatisfiableDisclosureSession", TestUnsatisfiableDisclosureSession)
t.Run("TestAttributeByteEncoding", TestAttributeByteEncoding)
t.Run("TestOutdatedClientIrmaConfiguration", TestOutdatedClientIrmaConfiguration)
t.Run("TestDisclosureNewAttributeUpdateSchemeManager", TestDisclosureNewAttributeUpdateSchemeManager)
t.Run("TestIssueNewAttributeUpdateSchemeManager", TestIssueNewAttributeUpdateSchemeManager)
t.Run("TestIssueOptionalAttributeUpdateSchemeManager", TestIssueOptionalAttributeUpdateSchemeManager)
t.Run("TestIssueNewCredTypeUpdateSchemeManager", TestIssueNewCredTypeUpdateSchemeManager)
t.Run("TestDisclosureNewCredTypeUpdateSchemeManager", TestDisclosureNewCredTypeUpdateSchemeManager)
t.Run("TestDisclosureNonexistingCredTypeUpdateSchemeManager", TestDisclosureNonexistingCredTypeUpdateSchemeManager)
t.Run("TestStaticQRSession", TestStaticQRSession)
t.Run("TestIssuedCredentialIsStored", TestIssuedCredentialIsStored)
t.Run("TestPOSTSizeLimit", TestPOSTSizeLimit)
t.Run("TestDisablePairing", TestDisablePairing)
}
......@@ -6,8 +6,10 @@ import (
"io/ioutil"
"os"
"path/filepath"
"reflect"
"testing"
"time"
"unsafe"
jwt "github.com/dgrijalva/jwt-go"
irma "github.com/privacybydesign/irmago"
......@@ -18,6 +20,9 @@ import (
"github.com/stretchr/testify/require"
)
// Defines the maximum protocol version of an irmaclient in tests
var maxClientVersion = &irma.ProtocolVersion{Major: 2, Minor: 8}
func TestMain(m *testing.M) {
// Create HTTP server for scheme managers
test.StartSchemeManagerHttpServer()
......@@ -44,6 +49,12 @@ func parseExistingStorage(t *testing.T, storage string) (*irmaclient.Client, *Te
handler,
)
require.NoError(t, err)
// Set max version we want to test on
version := extractClientMaxVersion(client)
version.Major = maxClientVersion.Major
version.Minor = maxClientVersion.Minor
client.SetPreferences(irmaclient.Preferences{DeveloperMode: true})
return client, handler
}
......@@ -120,7 +131,7 @@ func getMultipleIssuanceRequest() *irma.IssuanceRequest {
var TestType = "irmaserver-jwt"
func startSession(t *testing.T, request irma.SessionRequest, sessiontype string) *server.SessionPackage {
func startSession(t *testing.T, request irma.SessionRequest, sessiontype string) (*server.SessionPackage, *irma.FrontendSessionRequest) {
var (
sesPkg server.SessionPackage
err error
......@@ -141,7 +152,7 @@ func startSession(t *testing.T, request irma.SessionRequest, sessiontype string)
}
require.NoError(t, err)
return &sesPkg
return &sesPkg, sesPkg.FrontendRequest
}
func getJwt(t *testing.T, request irma.SessionRequest, sessiontype string, alg jwt.SigningMethod) string {
......@@ -189,7 +200,14 @@ func getJwt(t *testing.T, request irma.SessionRequest, sessiontype string, alg j
return j
}
func sessionHelper(t *testing.T, request irma.SessionRequest, sessiontype string, client *irmaclient.Client) string {
func sessionHelperWithFrontendOptions(
t *testing.T,
request irma.SessionRequest,
sessiontype string,
client *irmaclient.Client,
frontendOptionsHandler func(handler *TestHandler),
pairingHandler func(handler *TestHandler),
) string {
if client == nil {
var handler *TestClientHandler
client, handler = parseStorage(t)
......@@ -201,25 +219,70 @@ func sessionHelper(t *testing.T, request irma.SessionRequest, sessiontype string
defer StopRequestorServer()
}
sesPkg := startSession(t, request, sessiontype)
sesPkg, frontendRequest := startSession(t, request, sessiontype)
c := make(chan *SessionResult)
h := &TestHandler{t: t, c: c, client: client, expectedServerName: expectedRequestorInfo(t, client.Configuration)}
h := &TestHandler{
t: t,
c: c,
client: client,
expectedServerName: expectedRequestorInfo(t, client.Configuration),
}
if frontendOptionsHandler != nil || pairingHandler != nil {
h.pairingCodeChan = make(chan string)
h.frontendTransport = irma.NewHTTPTransport(sesPkg.SessionPtr.URL, false)
h.frontendTransport.SetHeader(irma.AuthorizationHeader, string(frontendRequest.Authorization))
}
if frontendOptionsHandler != nil {
frontendOptionsHandler(h)
}
qrjson, err := json.Marshal(sesPkg.SessionPtr)
require.NoError(t, err)
client.NewSession(string(qrjson), h)
h.dismisser = client.NewSession(string(qrjson), h)
if pairingHandler != nil {
pairingHandler(h)
}
if result := <-c; result != nil {
require.NoError(t, result.Err)
}
var resJwt string
err = irma.NewHTTPTransport("http://localhost:48682/session/"+sesPkg.Token, false).Get("result-jwt", &resJwt)
err = irma.NewHTTPTransport("http://localhost:48682/session/"+string(sesPkg.Token), false).Get("result-jwt", &resJwt)
require.NoError(t, err)
return resJwt
}
func sessionHelper(t *testing.T, request irma.SessionRequest, sessiontype string, client *irmaclient.Client) string {
return sessionHelperWithFrontendOptions(t, request, sessiontype, client, nil, nil)
}
func extractClientTransport(dismisser irmaclient.SessionDismisser) *irma.HTTPTransport {
return extractPrivateField(dismisser, "transport").(*irma.HTTPTransport)
}
func extractClientMaxVersion(client *irmaclient.Client) *irma.ProtocolVersion {
return extractPrivateField(client, "maxVersion").(*irma.ProtocolVersion)
}
func extractPrivateField(i interface{}, field string) interface{} {
rct := reflect.ValueOf(i).Elem().FieldByName(field)
return reflect.NewAt(rct.Type(), unsafe.Pointer(rct.UnsafeAddr())).Elem().Interface()
}
func setPairingMethod(method irma.PairingMethod, handler *TestHandler) string {
optionsRequest := irma.NewFrontendOptionsRequest()
optionsRequest.PairingMethod = method
options := &irma.SessionOptions{}
err := handler.frontendTransport.Post("frontend/options", options, optionsRequest)
require.NoError(handler.t, err)
return options.PairingCode
}
func expectedRequestorInfo(t *testing.T, conf *irma.Configuration) *irma.RequestorInfo {
if common.ForceHTTPS {
return irma.NewRequestorInfo("localhost")
......
package sessiontest
import (
"bytes"
"encoding/json"
"io/ioutil"
"net/http"
"reflect"
"testing"
"time"
......@@ -60,7 +57,7 @@ func requestorSessionHelper(t *testing.T, request interface{}, client *irmaclien
clientChan := make(chan *SessionResult, 2)
serverChan := make(chan *server.SessionResult)
qr, token, err := irmaServer.StartSession(request, func(result *server.SessionResult) {
qr, requestorToken, _, err := irmaServer.StartSession(request, func(result *server.SessionResult) {
serverChan <- result
})
require.NoError(t, err)
......@@ -68,13 +65,13 @@ func requestorSessionHelper(t *testing.T, request interface{}, client *irmaclien
var h irmaclient.Handler
requestor := expectedRequestorInfo(t, client.Configuration)
if opts&sessionOptionUnsatisfiableRequest > 0 {
h = &UnsatisfiableTestHandler{TestHandler: TestHandler{t, clientChan, client, requestor, 0, ""}}
h = &UnsatisfiableTestHandler{TestHandler: TestHandler{t, clientChan, client, requestor, 0, "", nil, nil, nil}}
} else {
var wait time.Duration = 0
if opts&sessionOptionClientWait > 0 {
wait = 2 * time.Second
}
h = &TestHandler{t, clientChan, client, requestor, wait, ""}
h = &TestHandler{t, clientChan, client, requestor, wait, "", nil, nil, nil}
}
j, err := json.Marshal(qr)
......@@ -91,19 +88,12 @@ func requestorSessionHelper(t *testing.T, request interface{}, client *irmaclien
}
serverResult := <-serverChan
require.Equal(t, token, serverResult.Token)
require.Equal(t, requestorToken, serverResult.Token)
if opts&sessionOptionRetryPost > 0 {
req, err := http.NewRequest(http.MethodPost,
qr.URL+"/proofs",
bytes.NewBuffer([]byte(h.(*TestHandler).result)),
)
require.NoError(t, err)
req.Header.Add("Content-Type", "application/json")
res, err := new(http.Client).Do(req)
require.NoError(t, err)
require.True(t, res.StatusCode < 300)
_, err = ioutil.ReadAll(res.Body)
clientTransport := extractClientTransport(dismisser)
var result string
err := clientTransport.Post("proofs", &result, h.(*TestHandler).result)
require.NoError(t, err)
}
......@@ -114,7 +104,7 @@ func requestorSessionHelper(t *testing.T, request interface{}, client *irmaclien
func TestRequestorInvalidRequest(t *testing.T) {
StartIrmaServer(t, false, "")
defer StopIrmaServer()
_, _, err := irmaServer.StartSession(irma.NewDisclosureRequest(
_, _, _, err := irmaServer.StartSession(irma.NewDisclosureRequest(
irma.NewAttributeTypeIdentifier("irma-demo.RU.foo.bar"),
irma.NewAttributeTypeIdentifier("irma-demo.baz.qux.abc"),
), nil)
......@@ -124,7 +114,7 @@ func TestRequestorInvalidRequest(t *testing.T) {
func TestRequestorDoubleGET(t *testing.T) {
StartIrmaServer(t, false, "")
defer StopIrmaServer()
qr, _, err := irmaServer.StartSession(irma.NewDisclosureRequest(
qr, _, _, err := irmaServer.StartSession(irma.NewDisclosureRequest(
irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID"),
), nil)
require.NoError(t, err)
......@@ -226,7 +216,7 @@ func TestRequestorCombinedSessionMultipleAttributes(t *testing.T) {
]
}`), &ir))
require.Equal(t, server.StatusDone, requestorSessionHelper(t, &ir, nil).Status)
require.Equal(t, irma.ServerStatusDone, requestorSessionHelper(t, &ir, nil).Status)
}
func testRequestorIssuance(t *testing.T, keyshare bool, client *irmaclient.Client) {
......@@ -378,12 +368,12 @@ func TestClientDeveloperMode(t *testing.T) {
// Try to start another session with our non-https server
issuanceRequest = getNameIssuanceRequest()
qr, _, err := irmaServer.StartSession(issuanceRequest, nil)
qr, _, _, err := irmaServer.StartSession(issuanceRequest, nil)
require.NoError(t, err)
c := make(chan *SessionResult, 1)
j, err := json.Marshal(qr)
require.NoError(t, err)
client.NewSession(string(j), &TestHandler{t, c, client, nil, 0, ""})
client.NewSession(string(j), &TestHandler{t, c, client, nil, 0, "", nil, nil, nil})
result := <-c
// Check that it failed with an appropriate error message
......@@ -460,10 +450,10 @@ func TestIssueExpiredKey(t *testing.T) {
expireKey(t, client.Configuration)
result = requestorSessionHelper(t, getIssuanceRequest(true), client, sessionOptionReuseServer, sessionOptionIgnoreError)
require.Nil(t, result.Err)
require.Equal(t, server.StatusCancelled, result.Status)
require.Equal(t, irma.ServerStatusCancelled, result.Status)
// server aborts issuance sessions in case of expired public keys
expireKey(t, irmaServerConfiguration.IrmaConfiguration)
_, _, err := irmaServer.StartSession(getIssuanceRequest(true), nil)
_, _, _, err := irmaServer.StartSession(getIssuanceRequest(true), nil)
require.Error(t, err)
}
......@@ -138,6 +138,24 @@ func TestRevocationAll(t *testing.T) {
require.NotEmpty(t, result.Missing)
})
t.Run("MixRevocationNonRevocation", func(t *testing.T) {
client, handler := revocationSetup(t)
defer test.ClearTestStorage(t, handler.storage)
defer stopRevocationServer()
request := revocationRequest(revocationTestAttr)
request.Disclose[0][0] = append(request.Disclose[0][0], irma.NewAttributeRequest("irma-demo.RU.studentCard.studentID"))
result := revocationSession(t, client, request)
require.Equal(t, irma.ProofStatusValid, result.ProofStatus)
require.NotEmpty(t, result.Disclosed)
request = revocationRequest(revocationTestAttr)
request.Disclose = append(request.Disclose, irma.AttributeDisCon{{irma.NewAttributeRequest("irma-demo.RU.studentCard.studentID")}})
result = revocationSession(t, client, request)
require.Equal(t, irma.ProofStatusValid, result.ProofStatus)
require.NotEmpty(t, result.Disclosed)
})
t.Run("AttributeBasedSignature", func(t *testing.T) {
client, handler := revocationSetup(t)
defer test.ClearTestStorage(t, handler.storage)
......@@ -219,7 +237,7 @@ func TestRevocationAll(t *testing.T) {
stopRevocationServer()
result := revocationSession(t, client, nil, sessionOptionIgnoreError)
require.Equal(t, server.StatusCancelled, result.Status)
require.Equal(t, irma.ServerStatusCancelled, result.Status)
require.NotNil(t, result.Err)
require.Equal(t, result.Err.ErrorName, string(server.ErrorRevocation.Type))
})
......
......@@ -136,6 +136,47 @@ func TestIssuanceSameAttributesNotSingleton(t *testing.T) {
require.Equal(t, prevLen+1, len(client.CredentialInfoList()))
}
func TestIssuancePairing(t *testing.T) {
id := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")
request := getCombinedIssuanceRequest(id)
var pairingCode string
frontendOptionsHandler := func(handler *TestHandler) {
pairingCode = setPairingMethod(irma.PairingMethodPin, handler)
}
pairingHandler := func(handler *TestHandler) {
// Below protocol version 2.8 pairing is not supported, so then the pairing stage is expected to be skipped.
if extractClientMaxVersion(handler.client).Below(2, 8) {
return
}
require.Equal(t, pairingCode, <-handler.pairingCodeChan)
// Check whether access to request endpoint is denied as long as pairing is not finished
clientTransport := extractClientTransport(handler.dismisser)
err := clientTransport.Get("request", struct{}{})
require.Error(t, err)
sessionErr := err.(*irma.SessionError)
require.Equal(t, irma.ErrorApi, sessionErr.ErrorType)
require.Equal(t, server.ErrorPairingRequired.Status, sessionErr.RemoteError.Status)
require.Equal(t, string(server.ErrorPairingRequired.Type), sessionErr.RemoteError.ErrorName)
// Check whether pairing cannot be disabled again after client is connected.
request := irma.NewFrontendOptionsRequest()
result := &irma.SessionOptions{}
err = handler.frontendTransport.Post("frontend/options", result, request)
require.Error(t, err)
sessionErr = err.(*irma.SessionError)
require.Equal(t, irma.ErrorApi, sessionErr.ErrorType)
require.Equal(t, server.ErrorUnexpectedRequest.Status, sessionErr.RemoteError.Status)
require.Equal(t, string(server.ErrorUnexpectedRequest.Type), sessionErr.RemoteError.ErrorName)
err = handler.frontendTransport.Post("frontend/pairingcompleted", nil, nil)
require.NoError(t, err)
}
sessionHelperWithFrontendOptions(t, request, "issue", nil, frontendOptionsHandler, pairingHandler)
}