...
 
Commits (31)
......@@ -4,6 +4,45 @@ 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.5.0-rc.3] - 2020-05-14
### Added
* Various additions to `irmaclient` for the [new IRMA app](https://github.com/privacybydesign/irmamobile), among others:
* Several new fields in `irma.CredentialType` for specifying e.g. help messages and card colors
* Added developer mode enabling non-HTTPS connections to IRMA servers for local testing (see below)
### Fixed
* Problems with `--privkeys` option to IRMA server
### Security
* `irma` command, IRMA server and `irmaclient` will now enforce HTTPS for outgoing connections whenever possible
* Update supported TLS ciphers and curves for IRMA server
* Fixed potential bug allowing MitM attacker to arbitrarily change installed schemes
* Fixed potential DoS attack in IRMA server endpoints (sending it large amounts of data or keeping connections open indefinitely)
## [0.5.0-rc.2] - 2020-04-21
### Added
* Revocation of previously issued credentials (see [documentation](https://irma.app/docs/revocation/))
* Support HTTP/2 in IRMA server and app
* Option `--skip-permission-keys-check` to IRMA server disabling checking that all required private keys are present in the server configuration
### Changed
* Use go modules instead of `dep` for tracking and locking dependencies
### Fixed
* `irmaserver` HTTP handler returns 404 an 405 as JSON error messages as expected
* Consistently use a docopt/git/aptitude like format for usage sections in help of `irma` subcommands
* Incorrect default value of `--url` flag to `irma session` subcommand
* IRMA server no longer allows nonsensical wildcard usage in [requestor permissions](https://irma.app/docs/irma-server/#permissions)
### Security
* `irma issuer keygen` now has default keylength 2048
* Added various sanity checks to files and file paths
* Fixed potential scheme downgrade attack when installing/updating schemes in MitM scenarios
## [0.5.0-rc.1] - 2020-03-03
### Added
- Include `clientReturnURL` in session request
......@@ -21,6 +60,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `irma server` no longer crashes at startup if no network interfaces are available
- Various bugs in `irma server` configuration
## [0.4.1] - 2019-10-15
### Changed
- Renamed `irma session` flag `--authmethod` to `--auth-method` for consistency with server `Configuration` struct
......@@ -41,6 +81,8 @@ 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.5.0-rc.3]: https://github.com/privacybydesign/irmago/compare/v0.5.0-rc.2...v0.5.0-rc.3
[0.5.0-rc.2]: https://github.com/privacybydesign/irmago/compare/v0.5.0-rc.1...v0.5.0-rc.2
[0.5.0-rc.1]: https://github.com/privacybydesign/irmago/compare/v0.4.1...v0.5.0-rc.1
[0.4.1]: https://github.com/privacybydesign/irmago/compare/v0.4.0...v0.4.1
[0.4.0]: https://github.com/privacybydesign/irmago/tree/v0.4.0
......@@ -4,11 +4,12 @@ import (
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"errors"
"time"
"github.com/privacybydesign/gabi"
"github.com/privacybydesign/gabi/big"
"github.com/go-errors/errors"
)
const (
......@@ -41,27 +42,35 @@ type MetadataAttribute struct {
// AttributeList contains attributes, excluding the secret key,
// providing convenient access to the metadata attribute.
type AttributeList struct {
*MetadataAttribute `json:"-"`
Ints []*big.Int
Revoked bool `json:",omitempty"`
strings []TranslatedString
attrMap map[AttributeTypeIdentifier]TranslatedString
info *CredentialInfo
h string
*MetadataAttribute `json:"-"`
Ints []*big.Int
Revoked bool `json:",omitempty"`
RevocationSupported bool `json:,omitempty`
strings []TranslatedString
attrMap map[AttributeTypeIdentifier]TranslatedString
info *CredentialInfo
h string
}
// NewAttributeListFromInts initializes a new AttributeList from a list of bigints.
func NewAttributeListFromInts(ints []*big.Int, conf *Configuration) *AttributeList {
al := &AttributeList{
Ints: ints,
MetadataAttribute: MetadataFromInt(ints[0], conf),
metadata := MetadataFromInt(ints[0], conf)
credtype := metadata.CredentialType()
idx := credtype.RevocationIndex + 1
var rev bool
if credtype != nil {
rev = len(ints) > idx && ints[idx] != nil && ints[idx].Cmp(bigZero) != 0
}
return &AttributeList{
Ints: ints,
MetadataAttribute: metadata,
RevocationSupported: rev,
}
return al
}
func (al *AttributeList) Info() *CredentialInfo {
if al.info == nil {
al.info = NewCredentialInfo(al.Ints, al.Conf)
al.info = al.CredentialInfo()
}
al.info.Revoked = al.Revoked
return al.info
......@@ -95,11 +104,11 @@ func (al *AttributeList) Hash() string {
return al.h
}
func (al *AttributeList) Map(conf *Configuration) map[AttributeTypeIdentifier]TranslatedString {
func (al *AttributeList) Map() map[AttributeTypeIdentifier]TranslatedString {
if al.attrMap == nil {
al.attrMap = make(map[AttributeTypeIdentifier]TranslatedString)
ctid := al.CredentialType().Identifier()
attrTypes := conf.CredentialTypes[ctid].AttributeTypes
attrTypes := al.Conf.CredentialTypes[ctid].AttributeTypes
for i, val := range al.Strings() {
if attrTypes[i].RevocationAttribute {
continue
......
......@@ -4,43 +4,41 @@ import (
"fmt"
"strings"
"time"
"github.com/privacybydesign/gabi/big"
)
// CredentialInfo contains all information of an IRMA credential.
type CredentialInfo struct {
ID string // e.g., "studentCard"
IssuerID string // e.g., "RU"
SchemeManagerID string // e.g., "irma-demo"
SignedOn Timestamp // Unix timestamp
Expires Timestamp // Unix timestamp
Attributes map[AttributeTypeIdentifier]TranslatedString // Human-readable rendered attributes
Hash string // SHA256 hash over the attributes
Revoked bool // If the credential has been revoked
ID string // e.g., "studentCard"
IssuerID string // e.g., "RU"
SchemeManagerID string // e.g., "irma-demo"
SignedOn Timestamp // Unix timestamp
Expires Timestamp // Unix timestamp
Attributes map[AttributeTypeIdentifier]TranslatedString // Human-readable rendered attributes
Hash string // SHA256 hash over the attributes
Revoked bool // If the credential has been revoked
RevocationSupported bool // If the credential supports creating nonrevocation proofs
}
// A CredentialInfoList is a list of credentials (implements sort.Interface).
type CredentialInfoList []*CredentialInfo
func NewCredentialInfo(ints []*big.Int, conf *Configuration) *CredentialInfo {
meta := MetadataFromInt(ints[0], conf)
credtype := meta.CredentialType()
func (attrs *AttributeList) CredentialInfo() *CredentialInfo {
credtype := attrs.CredentialType()
if credtype == nil {
return nil
}
attrs := NewAttributeListFromInts(ints, conf)
id := credtype.Identifier()
issid := id.IssuerIdentifier()
return &CredentialInfo{
ID: id.Name(),
IssuerID: issid.Name(),
SchemeManagerID: issid.SchemeManagerIdentifier().Name(),
SignedOn: Timestamp(meta.SigningDate()),
Expires: Timestamp(meta.Expiry()),
Attributes: attrs.Map(conf),
Hash: attrs.Hash(),
ID: id.Name(),
IssuerID: issid.Name(),
SchemeManagerID: issid.SchemeManagerIdentifier().Name(),
SignedOn: Timestamp(attrs.SigningDate()),
Expires: Timestamp(attrs.Expiry()),
Attributes: attrs.Map(),
Hash: attrs.Hash(),
Revoked: attrs.Revoked,
RevocationSupported: attrs.RevocationSupported,
}
}
......
......@@ -69,10 +69,25 @@ type CredentialType struct {
RevocationServers []string `xml:"RevocationServers>RevocationServer"`
RevocationUpdateCount uint64
RevocationUpdateSpeed uint64
RevocationIndex int `xml:"-"`
XMLVersion int `xml:"version,attr"`
XMLName xml.Name `xml:"IssueSpecification"`
IssueURL TranslatedString `xml:"IssueURL"`
RevocationIndex int `xml:"-"`
XMLVersion int `xml:"version,attr"`
XMLName xml.Name `xml:"IssueSpecification"`
IssueURL TranslatedString `xml:"IssueURL"`
IsULIssueURL bool `xml:"IsULIssueURL"`
DeprecatedSince Timestamp
ForegroundColor string
BackgroundGradientStart string
BackgroundGradientEnd string
IsInCredentialStore bool
Category TranslatedString
FAQIntro TranslatedString
FAQPurpose TranslatedString
FAQContent TranslatedString
FAQHowto TranslatedString
Valid bool `xml:"-"`
}
......@@ -84,8 +99,9 @@ type AttributeType struct {
Name TranslatedString
Description TranslatedString
Index int `xml:"-"`
DisplayIndex *int `xml:"displayIndex,attr" json:",omitempty"`
Index int `xml:"-"`
DisplayIndex *int `xml:"displayIndex,attr" json:",omitempty"`
DisplayHint string `xml:"displayHint,attr" json:",omitempty"`
RevocationAttribute bool `xml:"revocation,attr" json:",omitempty"`
......
......@@ -3,7 +3,6 @@ module github.com/privacybydesign/irmago
go 1.13
require (
astuart.co/go-sse v0.0.0-20200223201439-6cc042ab6f6d
github.com/BurntSushi/toml v0.3.1 // indirect
github.com/alexandrevicenzi/go-sse v1.3.1-0.20200117161408-7b23d5ff7420
github.com/bwesterb/go-atum v1.0.0
......@@ -23,7 +22,6 @@ require (
github.com/jasonlvhit/gocron v0.0.0-20180312192515-54194c9749d4
github.com/jinzhu/gorm v1.9.12
github.com/lib/pq v1.3.0 // indirect
github.com/magiconair/properties v1.8.0 // indirect
github.com/mattn/go-colorable v0.0.9 // indirect
github.com/mattn/go-isatty v0.0.4 // indirect
github.com/mdp/qrterminal v1.0.1
......@@ -34,8 +32,9 @@ require (
github.com/onsi/ginkgo v1.12.0 // indirect
github.com/onsi/gomega v1.9.0 // indirect
github.com/pelletier/go-toml v1.2.0 // indirect
github.com/pkg/errors v0.8.0
github.com/pkg/errors v0.8.1
github.com/privacybydesign/gabi v0.0.0-20200304102013-9df3e395ec4d
github.com/sietseringers/go-sse v0.0.0-20200223201439-6cc042ab6f6d
github.com/sirupsen/logrus v1.2.0
github.com/spf13/afero v1.2.0 // indirect
github.com/spf13/cast v1.3.0
......@@ -43,7 +42,7 @@ require (
github.com/spf13/jwalterweatherman v1.0.0 // indirect
github.com/spf13/pflag v1.0.4-0.20190111213756-a45bfec10d59
github.com/spf13/viper v1.0.1-0.20200205174444-d996804203c7
github.com/stretchr/testify v1.2.2
github.com/stretchr/testify v1.3.0
github.com/timshannon/bolthold v0.0.0-20190812165541-a85bcc049a2e // indirect
github.com/x-cray/logrus-prefixed-formatter v0.5.2
github.com/x448/float16 v0.8.4 // indirect
......@@ -51,8 +50,6 @@ require (
golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72 // indirect
)
replace astuart.co/go-sse => github.com/sietseringers/go-sse v0.0.0-20200223201439-6cc042ab6f6d
replace github.com/spf13/pflag => github.com/sietseringers/pflag v1.0.4-0.20190111213756-a45bfec10d59
replace github.com/spf13/viper => github.com/sietseringers/viper v1.0.1-0.20200205174444-d996804203c7
replace github.com/spf13/viper => github.com/sietseringers/viper v1.0.1-0.20200501103550-1e89975c9328
This diff is collapsed.
......@@ -17,6 +17,11 @@ import (
var Logger *logrus.Logger
// Disables HTTP forcing in irma.HTTPTransport for all instances,
// regardless of the instance's ForceHTTPS member.
// Only for use in unit tests.
var ForceHTTPS = true
// AssertPathExists returns nil only if it has been successfully
// verified that all specified paths exists.
func AssertPathExists(paths ...string) error {
......@@ -80,12 +85,18 @@ func EnsureDirectoryExists(path string) error {
// - first save the content in a temp file with a random filename in the same dir
// - then rename the temp file to the specified filepath, overwriting the old file
func SaveFile(fpath string, content []byte) (err error) {
fpath = filepath.FromSlash(fpath)
Logger.Debug("writing ", fpath)
info, exists, err := Stat(fpath)
if err != nil {
return err
}
if exists && (info.IsDir() || !info.Mode().IsRegular()) {
return errors.New("invalid destination path: not a file")
}
// Only accept 'simple' paths without . or .. or multiple separators
if fpath != filepath.Clean(fpath) {
return errors.New("invalid destination path")
}
......
......@@ -6,7 +6,7 @@ import (
"testing"
"time"
"github.com/pkg/errors"
"github.com/go-errors/errors"
"github.com/privacybydesign/irmago"
"github.com/privacybydesign/irmago/irmaclient"
"github.com/stretchr/testify/require"
......@@ -66,6 +66,13 @@ func (i *TestClientHandler) ChangePinBlocked(manager irma.SchemeManagerIdentifie
i.t.Fatal(err)
}
}
func (i *TestClientHandler) ReportError(err error) {
select {
case i.c <- err: //nop
default:
i.t.Fatal(err)
}
}
type TestHandler struct {
t *testing.T
......@@ -104,15 +111,23 @@ func (th TestHandler) Failure(err *irma.SessionError) {
}
}
func (th TestHandler) ClientReturnURLSet(clientReturnUrl string) {}
func (th TestHandler) UnsatisfiableRequest(request irma.SessionRequest, serverName irma.TranslatedString, missing irmaclient.MissingAttributes) {
th.Failure(&irma.SessionError{
ErrorType: irma.ErrorType("UnsatisfiableRequest"),
})
}
func (th TestHandler) RequestVerificationPermission(request *irma.DisclosureRequest, candidates [][][]*irma.AttributeIdentifier, ServerName irma.TranslatedString, callback irmaclient.PermissionHandler) {
func (th TestHandler) RequestVerificationPermission(request *irma.DisclosureRequest, satisfiable bool, candidates [][]irmaclient.DisclosureCandidates, ServerName irma.TranslatedString, callback irmaclient.PermissionHandler) {
if !satisfiable {
th.Failure(&irma.SessionError{ErrorType: irma.ErrorType("UnsatisfiableRequest")})
return
}
var choice irma.DisclosureChoice
for _, cand := range candidates {
choice.Attributes = append(choice.Attributes, cand[0])
var ids []*irma.AttributeIdentifier
var err error
for _, c := range cand {
ids, err = c.Choose()
if err == nil {
break
}
}
require.NoError(th.t, err)
choice.Attributes = append(choice.Attributes, ids)
}
if len(th.expectedServerName) != 0 {
require.Equal(th.t, th.expectedServerName, ServerName)
......@@ -122,11 +137,11 @@ func (th TestHandler) RequestVerificationPermission(request *irma.DisclosureRequ
}
callback(true, &choice)
}
func (th TestHandler) RequestIssuancePermission(request *irma.IssuanceRequest, candidates [][][]*irma.AttributeIdentifier, ServerName irma.TranslatedString, callback irmaclient.PermissionHandler) {
th.RequestVerificationPermission(&request.DisclosureRequest, candidates, ServerName, callback)
func (th TestHandler) RequestIssuancePermission(request *irma.IssuanceRequest, satisfiable bool, candidates [][]irmaclient.DisclosureCandidates, ServerName irma.TranslatedString, callback irmaclient.PermissionHandler) {
th.RequestVerificationPermission(&request.DisclosureRequest, satisfiable, candidates, ServerName, callback)
}
func (th TestHandler) RequestSignaturePermission(request *irma.SignatureRequest, candidates [][][]*irma.AttributeIdentifier, ServerName irma.TranslatedString, callback irmaclient.PermissionHandler) {
th.RequestVerificationPermission(&request.DisclosureRequest, candidates, ServerName, callback)
func (th TestHandler) RequestSignaturePermission(request *irma.SignatureRequest, satisfiable bool, candidates [][]irmaclient.DisclosureCandidates, ServerName irma.TranslatedString, callback irmaclient.PermissionHandler) {
th.RequestVerificationPermission(&request.DisclosureRequest, satisfiable, candidates, ServerName, callback)
}
func (th TestHandler) RequestSchemeManagerPermission(manager *irma.SchemeManager, callback func(proceed bool)) {
callback(true)
......@@ -139,21 +154,25 @@ type SessionResult struct {
Err error
SignatureResult *irma.SignedMessage
DisclosureResult *irma.Disclosure
Missing irmaclient.MissingAttributes
Missing [][]irmaclient.DisclosureCandidates
}
type UnsatisfiableTestHandler struct {
TestHandler
}
func (th UnsatisfiableTestHandler) UnsatisfiableRequest(request irma.SessionRequest, serverName irma.TranslatedString, missing irmaclient.MissingAttributes) {
th.c <- &SessionResult{Missing: missing}
}
func (th UnsatisfiableTestHandler) Success(result string) {
th.Failure(&irma.SessionError{ErrorType: irma.ErrorType("Unsatisfiable request succeeded")})
}
func (th UnsatisfiableTestHandler) RequestVerificationPermission(request *irma.DisclosureRequest, satisfiable bool, candidates [][]irmaclient.DisclosureCandidates, ServerName irma.TranslatedString, callback irmaclient.PermissionHandler) {
if satisfiable {
th.Failure(&irma.SessionError{ErrorType: irma.ErrorType("Unsatisfiable request succeeded")})
return
}
th.c <- &SessionResult{Missing: candidates}
}
// ManualTestHandler embeds a TestHandler to inherit its methods.
// Below we overwrite the methods that require behaviour specific to manual settings.
type ManualTestHandler struct {
......@@ -191,10 +210,10 @@ func (th *ManualTestHandler) Success(result string) {
th.c <- retval
}
func (th *ManualTestHandler) RequestSignaturePermission(request *irma.SignatureRequest, candidates [][][]*irma.AttributeIdentifier, requesterName irma.TranslatedString, ph irmaclient.PermissionHandler) {
th.RequestVerificationPermission(&request.DisclosureRequest, candidates, requesterName, ph)
func (th *ManualTestHandler) RequestSignaturePermission(request *irma.SignatureRequest, satisfiable bool, candidates [][]irmaclient.DisclosureCandidates, requesterName irma.TranslatedString, ph irmaclient.PermissionHandler) {
th.RequestVerificationPermission(&request.DisclosureRequest, satisfiable, candidates, requesterName, ph)
}
func (th *ManualTestHandler) RequestIssuancePermission(request *irma.IssuanceRequest, candidates [][][]*irma.AttributeIdentifier, issuerName irma.TranslatedString, ph irmaclient.PermissionHandler) {
func (th *ManualTestHandler) RequestIssuancePermission(request *irma.IssuanceRequest, satisfiable bool, candidates [][]irmaclient.DisclosureCandidates, issuerName irma.TranslatedString, ph irmaclient.PermissionHandler) {
ph(true, nil)
}
......@@ -202,10 +221,23 @@ func (th *ManualTestHandler) RequestIssuancePermission(request *irma.IssuanceReq
func (th *ManualTestHandler) RequestSchemeManagerPermission(manager *irma.SchemeManager, callback func(proceed bool)) {
th.Failure(&irma.SessionError{Err: errors.New("Unexpected session type")})
}
func (th *ManualTestHandler) RequestVerificationPermission(request *irma.DisclosureRequest, candidates [][][]*irma.AttributeIdentifier, verifierName irma.TranslatedString, ph irmaclient.PermissionHandler) {
func (th *ManualTestHandler) RequestVerificationPermission(request *irma.DisclosureRequest, satisfiable bool, candidates [][]irmaclient.DisclosureCandidates, verifierName irma.TranslatedString, ph irmaclient.PermissionHandler) {
if !satisfiable {
th.Failure(&irma.SessionError{ErrorType: irma.ErrorType("UnsatisfiableRequest")})
return
}
var choice irma.DisclosureChoice
for _, cand := range candidates {
choice.Attributes = append(choice.Attributes, cand[0])
var ids []*irma.AttributeIdentifier
var err error
for _, c := range cand {
ids, err = c.Choose()
if err == nil {
break
}
}
require.NoError(th.t, err)
choice.Attributes = append(choice.Attributes, ids)
}
ph(true, &choice)
}
......
......@@ -32,6 +32,7 @@ func TestKeyshareRegister(t *testing.T) {
require.NoError(t, client.KeyshareRemoveAll())
require.NoError(t, client.RemoveStorage())
client.SetPreferences(irmaclient.Preferences{DeveloperMode: true})
client.KeyshareEnroll(irma.NewSchemeManagerIdentifier("test"), nil, "12345", "en")
require.NoError(t, <-handler.c)
......
......@@ -12,6 +12,7 @@ import (
jwt "github.com/dgrijalva/jwt-go"
irma "github.com/privacybydesign/irmago"
"github.com/privacybydesign/irmago/internal/common"
"github.com/privacybydesign/irmago/internal/test"
"github.com/privacybydesign/irmago/irmaclient"
"github.com/privacybydesign/irmago/server"
......@@ -20,7 +21,7 @@ import (
)
func init() {
irma.ForceHttps = false
common.ForceHTTPS = false // globally disable https enforcement
irma.Logger.SetLevel(logrus.WarnLevel)
}
......@@ -50,6 +51,7 @@ func parseExistingStorage(t *testing.T, storage string) (*irmaclient.Client, *Te
handler,
)
require.NoError(t, err)
client.SetPreferences(irmaclient.Preferences{DeveloperMode: true})
return client, handler
}
......@@ -135,19 +137,19 @@ func startSession(t *testing.T, request irma.SessionRequest, sessiontype string)
switch TestType {
case "apiserver":
url := "http://localhost:8088/irma_api_server/api/v2/" + sessiontype
err = irma.NewHTTPTransport(url).Post("", qr, getJwt(t, request, sessiontype, jwt.SigningMethodNone))
err = irma.NewHTTPTransport(url, false).Post("", qr, getJwt(t, request, sessiontype, jwt.SigningMethodNone))
qr.URL = url + "/" + qr.URL
case "irmaserver-jwt":
url := "http://localhost:48682"
err = irma.NewHTTPTransport(url).Post("session", &sesPkg, getJwt(t, request, sessiontype, jwt.SigningMethodRS256))
err = irma.NewHTTPTransport(url, false).Post("session", &sesPkg, getJwt(t, request, sessiontype, jwt.SigningMethodRS256))
qr = sesPkg.SessionPtr
case "irmaserver-hmac-jwt":
url := "http://localhost:48682"
err = irma.NewHTTPTransport(url).Post("session", &sesPkg, getJwt(t, request, sessiontype, jwt.SigningMethodHS256))
err = irma.NewHTTPTransport(url, false).Post("session", &sesPkg, getJwt(t, request, sessiontype, jwt.SigningMethodHS256))
qr = sesPkg.SessionPtr
case "irmaserver":
url := "http://localhost:48682"
err = irma.NewHTTPTransport(url).Post("session", &sesPkg, request)
err = irma.NewHTTPTransport(url, false).Post("session", &sesPkg, request)
qr = sesPkg.SessionPtr
default:
t.Fatal("Invalid TestType")
......
......@@ -33,7 +33,7 @@ func manualSessionHelper(t *testing.T, client *irmaclient.Client, h *ManualTestH
bts, err := json.Marshal(request)
require.NoError(t, err)
client.NewSession(string(bts), h)
go client.NewSession(string(bts), h)
result := <-h.c
if result.Err != nil {
......
......@@ -10,6 +10,7 @@ import (
"time"
"github.com/privacybydesign/irmago"
"github.com/privacybydesign/irmago/internal/common"
"github.com/privacybydesign/irmago/internal/test"
"github.com/privacybydesign/irmago/irmaclient"
"github.com/privacybydesign/irmago/server"
......@@ -29,7 +30,7 @@ const (
type requestorSessionResult struct {
*server.SessionResult
Missing irmaclient.MissingAttributes
Missing [][]irmaclient.DisclosureCandidates
}
func processOptions(options ...sessionOption) sessionOption {
......@@ -49,7 +50,7 @@ func requestorSessionHelper(t *testing.T, request irma.SessionRequest, client *i
opts := processOptions(options...)
if opts&sessionOptionReuseServer == 0 {
StartIrmaServer(t, opts&sessionOptionUpdatedIrmaConfiguration > 0)
StartIrmaServer(t, opts&sessionOptionUpdatedIrmaConfiguration > 0, "")
defer StopIrmaServer()
}
......@@ -107,7 +108,7 @@ func requestorSessionHelper(t *testing.T, request irma.SessionRequest, client *i
// Check that nonexistent IRMA identifiers in the session request fail the session
func TestRequestorInvalidRequest(t *testing.T) {
StartIrmaServer(t, false)
StartIrmaServer(t, false, "")
defer StopIrmaServer()
_, _, err := irmaServer.StartSession(irma.NewDisclosureRequest(
irma.NewAttributeTypeIdentifier("irma-demo.RU.foo.bar"),
......@@ -117,7 +118,7 @@ func TestRequestorInvalidRequest(t *testing.T) {
}
func TestRequestorDoubleGET(t *testing.T) {
StartIrmaServer(t, false)
StartIrmaServer(t, false, "")
defer StopIrmaServer()
qr, _, err := irmaServer.StartSession(irma.NewDisclosureRequest(
irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID"),
......@@ -126,7 +127,7 @@ func TestRequestorDoubleGET(t *testing.T) {
// Simulate the first GET by the client in the session protocol, twice
var o interface{}
transport := irma.NewHTTPTransport(qr.URL)
transport := irma.NewHTTPTransport(qr.URL, false)
transport.SetHeader(irma.MinVersionHeader, "2.5")
transport.SetHeader(irma.MaxVersionHeader, "2.5")
require.NoError(t, transport.Get("", &o))
......@@ -352,3 +353,41 @@ func TestOptionalDisclosure(t *testing.T) {
require.True(t, reflect.DeepEqual(args.disclosed, result.Disclosed))
}
}
func TestClientDeveloperMode(t *testing.T) {
common.ForceHTTPS = true
defer func() { common.ForceHTTPS = false }()
client, handler := parseStorage(t)
defer test.ClearTestStorage(t, handler.storage)
StartIrmaServer(t, false, "")
defer StopIrmaServer()
// parseStorage returns a client with developer mode already enabled.
// Do a session with our local testserver (without https)
issuanceRequest := getNameIssuanceRequest()
requestorSessionHelper(t, issuanceRequest, client, sessionOptionReuseServer)
require.True(t, issuanceRequest.DevelopmentMode) // set to true by server
// RemoveStorage resets developer mode preference back to its default (disabled)
require.NoError(t, client.RemoveStorage())
require.False(t, client.Preferences.DeveloperMode)
// Try to start another session with our non-https server
issuanceRequest = getNameIssuanceRequest()
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, ""})
result := <-c
// Check that it failed with an appropriate error message
require.NotNil(t, result)
require.Error(t, result.Err)
serr, ok := result.Err.(*irma.SessionError)
require.True(t, ok)
require.NotNil(t, serr)
require.Equal(t, string(irma.ErrorHTTPS), string(serr.ErrorType))
require.Equal(t, "remote server does not use https", serr.Err.Error())
}
......@@ -85,14 +85,10 @@ func testRevocation(t *testing.T, attr irma.AttributeTypeIdentifier, client *irm
// client revocation callback was called
require.NotNil(t, handler.(*TestClientHandler).revoked)
require.Equal(t, credid, handler.(*TestClientHandler).revoked.Type)
// credential is no longer suggested as candidate
candidates, missing, err := client.Candidates(
revocationRequest(attr).Base(),
irma.AttributeDisCon{{{Type: attr}}},
)
// credential is no longer available as candidate
_, satisfiable, err := client.Candidates(request)
require.NoError(t, err)
require.Empty(t, candidates)
require.NotEmpty(t, missing)
require.False(t, satisfiable)
}
func TestRevocationAll(t *testing.T) {
......@@ -177,7 +173,7 @@ func TestRevocationAll(t *testing.T) {
t.Run("POSTUpdates", func(t *testing.T) {
startRevocationServer(t, true)
defer stopRevocationServer()
StartIrmaServer(t, false)
StartIrmaServer(t, false, "")
defer StopIrmaServer()
require.NoError(t, irmaServerConfiguration.IrmaConfiguration.Revocation.SyncDB(revocationTestCred))
......@@ -250,10 +246,12 @@ func TestRevocationAll(t *testing.T) {
require.Equal(t, uint64(0), events[len(events)-1].Index)
// Construct disclosure proof with nonrevocation proof against accumulator with index 0
candidates, missing, err := client.CheckSatisfiability(request)
candidates, satisfiable, err := client.Candidates(request)
require.NoError(t, err)
require.Empty(t, missing)
choice := &irma.DisclosureChoice{Attributes: [][]*irma.AttributeIdentifier{candidates[0][0]}}
require.True(t, satisfiable)
ids, err := candidates[0][0].Choose()
require.NoError(t, err)
choice := &irma.DisclosureChoice{Attributes: [][]*irma.AttributeIdentifier{ids}}
disclosure, _, err := client.Proofs(choice, request)
require.NoError(t, err)
proofAcc, err := disclosure.Proofs[0].(*gabi.ProofD).NonRevocationProof.SignedAccumulator.UnmarshalVerify(pk)
......@@ -339,7 +337,7 @@ func TestRevocationAll(t *testing.T) {
require.NoError(t, client.NonrevUpdateFromServer(revocationTestCred))
// Start an IRMA server and let it update at revocation server
StartIrmaServer(t, false)
StartIrmaServer(t, false, "")
defer StopIrmaServer()
conf = irmaServerConfiguration.IrmaConfiguration.Revocation
require.NoError(t, conf.SyncDB(revocationTestCred))
......@@ -362,7 +360,7 @@ func TestRevocationAll(t *testing.T) {
})
t.Run("SameIrmaServer", func(t *testing.T) {
StartIrmaServer(t, false)
StartIrmaServer(t, false, "")
defer StopIrmaServer()
// issue a credential, populating irmaServer's revocation memdb
......@@ -556,7 +554,7 @@ func TestRevocationAll(t *testing.T) {
// Start irma server and hackily temporarily disable revocation for our credtype
// by editing its irma.Configuration instance
StartIrmaServer(t, false)
StartIrmaServer(t, false, "")
defer StopIrmaServer()
conf := irmaServerConfiguration.IrmaConfiguration
credtyp := conf.CredentialTypes[revocationTestCred]
......
......@@ -53,24 +53,32 @@ func StopRequestorServer() {
requestorServer.Stop()
}
func StartIrmaServer(t *testing.T, updatedIrmaConf bool) {
func StartIrmaServer(t *testing.T, updatedIrmaConf bool, storage string) {
testdata := test.FindTestdataFolder(t)
irmaconf := "irma_configuration"
if updatedIrmaConf {
irmaconf += "_updated"
}
var err error
var assets string
path := filepath.Join(testdata, irmaconf)
if storage != "" {
assets = path
path = storage
}
irmaServerConfiguration = &server.Configuration{
URL: "http://localhost:48680",
Logger: logger,
DisableSchemesUpdate: true,
SchemesPath: filepath.Join(testdata, irmaconf),
URL: "http://localhost:48680",
Logger: logger,
DisableSchemesUpdate: true,
SchemesPath: path,
SchemesAssetsPath: assets,
IssuerPrivateKeysPath: filepath.Join(testdata, "privatekeys"),
RevocationSettings: irma.RevocationSettings{
revocationTestCred: {RevocationServerURL: "http://localhost:48683", SSE: true},
revKeyshareTestCred: {RevocationServerURL: "http://localhost:48683"},
},
}
var err error
irmaServer, err = irmaserver.New(irmaServerConfiguration)
require.NoError(t, err)
......
This diff is collapsed.
......@@ -4,7 +4,6 @@ package test
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
......@@ -14,6 +13,8 @@ import (
"time"
"github.com/privacybydesign/irmago/internal/common"
"github.com/go-errors/errors"
"github.com/stretchr/testify/require"
)
......
......@@ -92,7 +92,7 @@ func downloadSchemeManager(dest string, urls []string) error {
managerName := urlparts[len(urlparts)-1]
manager := irma.NewSchemeManager(managerName)
manager.URL = u
if err := conf.InstallSchemeManager(manager, nil); err != nil {
if err := conf.DangerousTOFUInstallSchemeManager(manager); err != nil {
return err
}
}
......
......@@ -25,12 +25,13 @@ package cmd
import (
"time"
"errors"
"fmt"
"regexp"
"strconv"
"github.com/privacybydesign/gabi"
"github.com/go-errors/errors"
"github.com/spf13/cobra"
)
......
......@@ -9,13 +9,13 @@ import (
"strings"
"time"
sseclient "astuart.co/go-sse"
"github.com/dgrijalva/jwt-go"
"github.com/go-errors/errors"
"github.com/mdp/qrterminal"
"github.com/privacybydesign/irmago"
"github.com/privacybydesign/irmago/internal/common"
"github.com/privacybydesign/irmago/server"
sseclient "github.com/sietseringers/go-sse"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
......
......@@ -51,7 +51,7 @@ func postRevocation(request *irma.RevocationRequest, url, schemespath, authmetho
die("credential type does not support revocation", nil)
}
transport := irma.NewHTTPTransport(url)
transport := irma.NewHTTPTransport(url, false)
switch authmethod {
case "none":
......
......@@ -180,7 +180,7 @@ func postRequest(serverurl string, request irma.RequestorRequest, name, authmeth
var (
err error
pkg = &server.SessionPackage{}
transport = irma.NewHTTPTransport(serverurl)
transport = irma.NewHTTPTransport(serverurl, false)
)
switch authmethod {
......
This diff is collapsed.
......@@ -18,7 +18,7 @@ var _ Handler = (*keyshareEnrollmentHandler)(nil)
// Session handlers in the order they are called
func (h *keyshareEnrollmentHandler) RequestIssuancePermission(request *irma.IssuanceRequest, candidates [][][]*irma.AttributeIdentifier, ServerName irma.TranslatedString, callback PermissionHandler) {
func (h *keyshareEnrollmentHandler) RequestIssuancePermission(request *irma.IssuanceRequest, satisfiable bool, candidates [][]DisclosureCandidates, ServerName irma.TranslatedString, callback PermissionHandler) {
// Fetch the username from the credential request and save it along with the scheme manager
for _, attr := range request.Credentials[0].Attributes {
h.kss.Username = attr
......@@ -56,10 +56,10 @@ func (h *keyshareEnrollmentHandler) fail(err error) {
func (h *keyshareEnrollmentHandler) StatusUpdate(action irma.Action, status irma.Status) {}
// The methods below should never be called, so we let each of them fail the session
func (h *keyshareEnrollmentHandler) RequestVerificationPermission(request *irma.DisclosureRequest, candidates [][][]*irma.AttributeIdentifier, ServerName irma.TranslatedString, callback PermissionHandler) {
func (h *keyshareEnrollmentHandler) RequestVerificationPermission(request *irma.DisclosureRequest, satisfiable bool, candidates [][]DisclosureCandidates, ServerName irma.TranslatedString, callback PermissionHandler) {
callback(false, nil)
}
func (h *keyshareEnrollmentHandler) RequestSignaturePermission(request *irma.SignatureRequest, candidates [][][]*irma.AttributeIdentifier, ServerName irma.TranslatedString, callback PermissionHandler) {
func (h *keyshareEnrollmentHandler) RequestSignaturePermission(request *irma.SignatureRequest, satisfiable bool, candidates [][]DisclosureCandidates, ServerName irma.TranslatedString, callback PermissionHandler) {
callback(false, nil)
}
func (h *keyshareEnrollmentHandler) RequestSchemeManagerPermission(manager *irma.SchemeManager, callback func(proceed bool)) {
......@@ -80,9 +80,6 @@ func (h *keyshareEnrollmentHandler) KeyshareEnrollmentDeleted(manager irma.Schem
func (h *keyshareEnrollmentHandler) KeyshareEnrollmentMissing(manager irma.SchemeManagerIdentifier) {
h.fail(errors.New("Keyshare enrollment failed: unenrolled"))
}
func (h *keyshareEnrollmentHandler) UnsatisfiableRequest(request irma.SessionRequest, ServerName irma.TranslatedString, missing MissingAttributes) {
h.fail(errors.New("Keyshare enrollment failed: unsatisfiable"))
}
func (h *keyshareEnrollmentHandler) ClientReturnURLSet(clientReturnURL string) {
h.fail(errors.New("Keyshare enrollment session unexpectedly found an external return url"))
}
......@@ -19,6 +19,5 @@ func TestConvertingLegacyStorage(t *testing.T) {
t.Run("TestCredentialInfoListNewAttribute", TestCredentialInfoListNewAttribute)
// TestFreshStorage is not needed, because this test does not use an existing storage
t.Run("TestKeyshareEnrollmentRemoval", TestKeyshareEnrollmentRemoval)
t.Run("TestUpdatePreferences", TestUpdatePreferences)
t.Run("TestUpdatingStorage", TestUpdatingStorage)
}
......@@ -2,7 +2,6 @@ package irmaclient
import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
......@@ -10,15 +9,18 @@ import (
"github.com/privacybydesign/gabi"
irma "github.com/privacybydesign/irmago"
"github.com/privacybydesign/irmago/internal/common"
"github.com/privacybydesign/irmago/internal/test"
"github.com/sirupsen/logrus"
"github.com/go-errors/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMain(m *testing.M) {
irma.Logger.SetLevel(logrus.FatalLevel)
common.ForceHTTPS = false // globally disable https enforcement
// Create HTTP server for scheme managers
test.StartSchemeManagerHttpServer()
......@@ -45,6 +47,7 @@ func parseExistingStorage(t *testing.T, storage string) (*Client, *TestClientHan
handler,
)
require.NoError(t, err)
client.SetPreferences(Preferences{DeveloperMode: true})
return client, handler
}
......@@ -120,52 +123,59 @@ func TestCandidates(t *testing.T) {
request := irma.NewDisclosureRequest(attrtype)
disjunction := request.Disclose[0]
request.ProtocolVersion = &irma.ProtocolVersion{Major: 2, Minor: 6}
attrs, missing, err := client.Candidates(request.Base(), disjunction)
attrs, satisfiable, err := client.candidatesDisCon(request.Base(), disjunction)
require.NoError(t, err)
require.Empty(t, missing)
require.True(t, satisfiable)
require.NotNil(t, attrs)
require.Len(t, attrs, 1)
require.NotNil(t, attrs[0])
require.Equal(t, attrs[0][0].Type, attrtype)
require.True(t, attrs[0][0].Present())
// If the disjunction requires our attribute to have 456 as value, which it does,
// then our attribute is a candidate
reqval := "456"
disjunction[0][0].Value = &reqval
attrs, missing, err = client.Candidates(request.Base(), disjunction)
attrs, satisfiable, err = client.candidatesDisCon(request.Base(), disjunction)
require.NoError(t, err)
require.Empty(t, missing)
require.True(t, satisfiable)
require.NotNil(t, attrs)
require.Len(t, attrs, 1)
require.NotNil(t, attrs[0])
require.Equal(t, attrs[0][0].Type, attrtype)
require.True(t, attrs[0][0].Present())
// If the disjunction requires our attribute to have a different value than it does,
// then it is NOT a match.
reqval = "foobarbaz"
disjunction[0][0].Value = &reqval
attrs, missing, err = client.Candidates(request.Base(), disjunction)
attrs, satisfiable, err = client.candidatesDisCon(request.Base(), disjunction)
require.NoError(t, err)
require.NotEmpty(t, missing)
require.False(t, satisfiable)
require.NotNil(t, attrs)
require.Empty(t, attrs)
require.Len(t, attrs, 1)
require.NotNil(t, attrs[0])
require.False(t, attrs[0][0].Present())
// A required value of nil counts as no requirement on the value, so our attribute is a candidate
disjunction[0][0].Value = nil
attrs, missing, err = client.Candidates(request.Base(), disjunction)
attrs, satisfiable, err = client.candidatesDisCon(request.Base(), disjunction)
require.NoError(t, err)
require.Empty(t, missing)
require.True(t, satisfiable)
require.NotNil(t, attrs)
require.Len(t, attrs, 1)
require.NotNil(t, attrs[0])
require.Equal(t, attrs[0][0].Type, attrtype)
require.True(t, attrs[0][0].Present())
// Require an attribute we do not have
disjunction[0][0] = irma.NewAttributeRequest("irma-demo.MijnOverheid.ageLower.over12")
attrs, missing, err = client.Candidates(request.Base(), disjunction)
attrs, satisfiable, err = client.candidatesDisCon(request.Base(), disjunction)
require.NoError(t, err)
require.NotEmpty(t, missing)
require.Empty(t, attrs)
require.False(t, satisfiable)
require.Len(t, attrs, 1)
require.NotNil(t, attrs[0])
require.False(t, attrs[0][0].Present())
}
func TestCandidateConjunctionOrder(t *testing.T) {
......@@ -194,9 +204,9 @@ func TestCandidateConjunctionOrder(t *testing.T) {
}
for i := 1; i < 20; i++ {
candidates, missing, err := client.CheckSatisfiability(req)
candidates, satisfiable, err := client.Candidates(req)
require.NoError(t, err)
require.Empty(t, missing)
require.True(t, satisfiable)
require.Equal(t, "irma-demo.RU.studentCard.level", candidates[0][0][0].Type.String())
}
}
......@@ -299,21 +309,6 @@ func TestKeyshareEnrollmentRemoval(t *testing.T) {
require.NotContains(t, client.keyshareServers, "test")
}
func TestUpdatePreferences(t *testing.T) {
client, handler := parseStorage(t)
defer test.ClearTestStorage(t, handler.storage)
client.SetCrashReportingPreference(!defaultPreferences.EnableCrashReporting)
client.applyPreferences()
err := client.storage.db.Close()
require.NoError(t, err)
client, handler = parseExistingStorage(t, handler.storage)
require.NoError(t, err)
require.Equal(t, false, client.Preferences.EnableCrashReporting)
}
func TestUpdatingStorage(t *testing.T) {
client, handler := parseStorage(t)
defer test.ClearTestStorage(t, handler.storage)
......@@ -403,3 +398,10 @@ func (i *TestClientHandler) ChangePinBlocked(manager irma.SchemeManagerIdentifie
i.t.Fatal(err)
}
}
func (i *TestClientHandler) ReportError(err error) {
select {
case i.c <- err: //nop
default:
i.t.Fatal(err)
}
}
......@@ -51,6 +51,7 @@ type keyshareSession struct {
issuerProofNonce *big.Int
timestamp *atum.Timestamp
pinCheck bool
preferences Preferences
}
type keyshareServer struct {
......@@ -153,10 +154,11 @@ func startKeyshareSession(
pin KeysharePinRequestor,
builders gabi.ProofBuilderList,
session irma.SessionRequest,
conf *irma.Configuration,
keyshareServers map[irma.SchemeManagerIdentifier]*keyshareServer,
issuerProofNonce *big.Int,
timestamp *atum.Timestamp,
conf *irma.Configuration,
keyshareServers map[irma.SchemeManagerIdentifier]*keyshareServer,
preferences Preferences,
) {
ksscount := 0
for managerID := range session.Identifiers().SchemeManagers {
......@@ -186,6 +188,7 @@ func startKeyshareSession(
issuerProofNonce: issuerProofNonce,
timestamp: timestamp,
pinCheck: false,
preferences: preferences,
}
for managerID := range session.Identifiers().SchemeManagers {
......@@ -195,7 +198,7 @@ func startKeyshareSession(
}
ks.keyshareServer = ks.keyshareServers[managerID]
transport := irma.NewHTTPTransport(scheme.KeyshareServer)
transport := irma.NewHTTPTransport(scheme.KeyshareServer, !ks.preferences.DeveloperMode)
transport.SetHeader(kssUsernameHeader, ks.keyshareServer.Username)
transport.SetHeader(kssAuthHeader, "Bearer "+ks.keyshareServer.token)
transport.SetHeader(kssVersionHeader, "2")
......
......@@ -9,7 +9,6 @@ import (
"sync"
"time"
"github.com/getsentry/raven-go"
"github.com/go-errors/errors"
"github.com/privacybydesign/gabi/revocation"
irma "github.com/privacybydesign/irmago"
......@@ -195,7 +194,7 @@ func (client *Client) nonrevApplyUpdates(id irma.CredentialTypeIdentifier, count
irma.Logger.WithField("credtype", id).Debug("scheduling nonrevocation cache update")
go func(cred *credential) {
if err := cred.NonrevPrepareCache(); err != nil {
raven.CaptureError(err, nil)
client.reportError(err)
}
}(cred)
}
......@@ -228,7 +227,7 @@ func (client *Client) nonrevPrepareCache(id irma.CredentialTypeIdentifier, index
func (client *Client) nonrevRepopulateCaches(request irma.SessionRequest) {
for id := range request.Disclosure().Identifiers().CredentialTypes {
credtype := client.Configuration.CredentialTypes[id]
if !credtype.RevocationSupported() {
if credtype == nil || !credtype.RevocationSupported() {
continue
}
for i := range client.attrs(id) {
......
This diff is collapsed.
......@@ -145,6 +145,8 @@ var clientUpdates = []func(client *Client) error{
return client.storage.TxStoreUpdates(tx, updates)
})
},
// TODO: Maybe delete preferences file to start afresh
}
// update performs any function from clientUpdates that has not
......
......@@ -61,7 +61,7 @@ type Configuration struct {
// (i.e., invalid signature, parsing error), and the problem that occurred when parsing them
DisabledSchemeManagers map[SchemeManagerIdentifier]*SchemeManagerError
Warnings []string
Warnings []string `json:"-"`
kssPublicKeys map[SchemeManagerIdentifier]map[int]*rsa.PublicKey
publicKeys map[IssuerIdentifier]map[uint]*gabi.PublicKey
......@@ -160,8 +160,10 @@ func (conf *Configuration) clear() {
conf.DisabledSchemeManagers = make(map[SchemeManagerIdentifier]*SchemeManagerError)
conf.kssPublicKeys = make(map[SchemeManagerIdentifier]map[int]*rsa.PublicKey)
conf.publicKeys = make(map[IssuerIdentifier]map[uint]*gabi.PublicKey)
conf.PrivateKeys = make(map[IssuerIdentifier]map[uint]*gabi.PrivateKey)
conf.reverseHashes = make(map[string]CredentialTypeIdentifier)
if conf.PrivateKeys == nil { // keep if already populated
conf.PrivateKeys = make(map[IssuerIdentifier]map[uint]*gabi.PrivateKey)
}
}
// ParseFolder populates the current Configuration by parsing the storage path,
......@@ -454,15 +456,6 @@ func (conf *Configuration) IsInitialized() bool {
return conf.initialized
}
// Prune removes any invalid scheme managers and everything they own from this Configuration
func (conf *Configuration) Prune() {
for _, manager := range conf.SchemeManagers {
if !manager.Valid {
_ = conf.RemoveSchemeManager(manager.Identifier(), false) // does not return errors
}
}
}
func (conf *Configuration) parseIssuerFolders(manager *SchemeManager, path string) error {
return common.IterateSubfolders(path, func(dir string, _ os.FileInfo) error {
issuer := &Issuer{}
......@@ -488,6 +481,10 @@ func (conf *Configuration) parseIssuerFolders(manager *SchemeManager, path strin
}
func (conf *Configuration) DeleteSchemeManager(id SchemeManagerIdentifier) error {
if conf.readOnly {
return errors.New("cannot delete scheme from a read-only configuration")
}
delete(conf.SchemeManagers, id)
delete(conf.DisabledSchemeManagers, id)
name := id.String()
......@@ -506,10 +503,8 @@ func (conf *Configuration) DeleteSchemeManager(id SchemeManagerIdentifier) error
delete(conf.CredentialTypes, cred)
}
}
if !conf.readOnly {
return os.RemoveAll(filepath.Join(conf.Path, id.Name()))
}
return nil
return os.RemoveAll(filepath.Join(conf.Path, id.Name()))
}
// parse $schememanager/$issuer/PublicKeys/$i.xml for $i = 1, ...
......@@ -727,7 +722,7 @@ func (conf *Configuration) CopyManagerFromAssets(scheme SchemeManagerIdentifier)
// DownloadSchemeManager downloads and returns a scheme manager description.xml file
// from the specified URL.
func DownloadSchemeManager(url string) (*SchemeManager, error) {
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
if !strings.HasPrefix(url, "https://") {
url = "https://" + url
}
if url[len(url)-1] == '/' {
......@@ -736,7 +731,7 @@ func DownloadSchemeManager(url string) (*SchemeManager, error) {
if strings.HasSuffix(url, "/description.xml") {
url = url[:len(url)-len("/description.xml")]
}
b, err := NewHTTPTransport(url).GetBytes("description.xml")
b, err := NewHTTPTransport(url, true).GetBytes("description.xml")
if err != nil {
return nil, err
}
......@@ -745,37 +740,9 @@ func DownloadSchemeManager(url string) (*SchemeManager, error) {
return nil, err
}
manager.URL = url // TODO?
return manager, nil
}
// RemoveSchemeManager removes the specified scheme manager and all associated issuers,
// public keys and credential types from this Configuration.
func (conf *Configuration) RemoveSchemeManager(id SchemeManagerIdentifier, fromStorage bool) error {
// Remove everything falling under the manager's responsibility
for credid := range conf.CredentialTypes {
if credid.IssuerIdentifier().SchemeManagerIdentifier() == id {
delete(conf.CredentialTypes, credid)
}
}
for issid := range conf.Issuers {
if issid.SchemeManagerIdentifier() == id {
delete(conf.Issuers, issid)
}
}
for issid := range conf.publicKeys {
if issid.SchemeManagerIdentifier() == id {
delete(conf.publicKeys, issid)
}
}
delete(conf.SchemeManagers, id)
if fromStorage || !conf.readOnly {
return os.RemoveAll(fmt.Sprintf("%s/%s", conf.Path, id.String()))
}
return nil
}
func (conf *Configuration) ReinstallSchemeManager(manager *SchemeManager) (err error) {
if conf.readOnly {
return errors.New("cannot install scheme into a read-only configuration")
......@@ -787,29 +754,42 @@ func (conf *Configuration) ReinstallSchemeManager(manager *SchemeManager) (err e
if err != nil {
return
}
pkbts, err := ioutil.ReadFile(filepath.Join(conf.Path, manager.ID, "pk.pem"))
if err != nil {
return err
}
if err = conf.DeleteSchemeManager(manager.Identifier()); err != nil {
return
}
err = conf.InstallSchemeManager(manager, nil)
err = conf.InstallSchemeManager(manager, pkbts)
return
}
// DangerousTOFUInstallSchemeManager downloads and adds the specified scheme to this Configuration,
// downloading and trusting its public key from the scheme's remote URL.
func (conf *Configuration) DangerousTOFUInstallSchemeManager(manager *SchemeManager) error {
return conf.installSchemeManager(manager, nil)
}
// InstallSchemeManager downloads and adds the specified scheme manager to this Configuration,
// provided its signature is valid.
// provided its signature is valid against the specified key.
func (conf *Configuration) InstallSchemeManager(manager *SchemeManager, publickey []byte) error {
if len(publickey) == 0 {
return errors.New("no public key specified")
}
return conf.installSchemeManager(manager, publickey)
}
func (conf *Configuration) installSchemeManager(manager *SchemeManager, publickey []byte) error {
if conf.readOnly {
return errors.New("cannot install scheme into a read-only configuration")
}
name := manager.ID
if err := common.EnsureDirectoryExists(filepath.Join(conf.Path, name)); err != nil {
return err
}
t := NewHTTPTransport(manager.URL, true)
t := NewHTTPTransport(manager.URL)
if err := conf.downloadFile(t, name, "description.xml"); err != nil {
return err
}
if publickey != nil {
if err := common.SaveFile(filepath.Join(conf.Path, name, "pk.pem"), publickey); err != nil {
return err
......@@ -819,6 +799,10 @@ func (conf *Configuration) InstallSchemeManager(manager *SchemeManager, publicke
return err
}
}
if err := conf.downloadFile(t, name, "description.xml"); err != nil {
return err
}
if err := conf.DownloadSchemeManagerSignature(manager); err != nil {
return err
}
......@@ -837,7 +821,7 @@ func (conf *Configuration) DownloadSchemeManagerSignature(manager *SchemeManager
return errors.New("cannot download into a read-only configuration")
}
t := NewHTTPTransport(manager.URL)
t := NewHTTPTransport(manager.URL, true)
if err = conf.downloadFile(t, manager.ID, "index"); err != nil {
return
}
......@@ -1242,7 +1226,7 @@ func (conf *Configuration) UpdateSchemeManager(id SchemeManagerIdentifier, downl
}
// Check remote timestamp, verify it against the new index, and see if we have to do anything
transport := NewHTTPTransport(manager.URL + "/")
transport := NewHTTPTransport(manager.URL+"/", true)
err = conf.downloadSignedFile(transport, manager.ID, "timestamp", newIndex[manager.ID+"/timestamp"])
if err != nil {
return err
......
......@@ -16,6 +16,10 @@ import (
"github.com/stretchr/testify/require"
)
func init() {
common.ForceHTTPS = false // globally disable https enforcement
}
func parseConfiguration(t *testing.T) *Configuration {
conf, err := NewConfiguration("testdata/irma_configuration", ConfigurationOptions{})
require.NoError(t, err)
......@@ -69,7 +73,7 @@ func TestRetryHTTPRequest(t *testing.T) {
test.StartBadHttpServer(2, 1*time.Second, "42")
defer test.StopBadHttpServer()
transport := NewHTTPTransport("http://localhost:48682")
transport := NewHTTPTransport("http://localhost:48682", false)
transport.client.HTTPClient.Timeout = 500 * time.Millisecond
bts, err := transport.GetBytes("")
require.NoError(t, err)
......
......@@ -18,9 +18,6 @@ import (
// Status encodes the status of an IRMA session (e.g., connected).
type Status string
// disabled until we offer a convenient way to toggle this in irma_mobile
var ForceHttps bool = false
const (
MinVersionHeader = "X-IRMA-MinProtocolVersion"
MaxVersionHeader = "X-IRMA-MaxProtocolVersion"
......@@ -167,8 +164,6 @@ type Qr struct {
Type Action `json:"irmaqr"`
}
type SchemeManagerRequest Qr
// Statuses
const (
StatusConnected = Status("connected")
......@@ -178,13 +173,12 @@ const (
// Actions
const (
ActionSchemeManager = Action("schememanager")
ActionDisclosing = Action("disclosing")
ActionSigning = Action("signing")
ActionIssuing = Action("issuing")
ActionRedirect = Action("redirect")
ActionRevoking = Action("revoking")
ActionUnknown = Action("unknown")
ActionDisclosing = Action("disclosing")
ActionSigning = Action("signing")
ActionIssuing = Action("issuing")
ActionRedirect = Action("redirect")
ActionRevoking = Action("revoking")
ActionUnknown = Action("unknown")
)
// Protocol errors
......@@ -193,6 +187,8 @@ const (
ErrorProtocolVersionNotSupported = ErrorType("protocolVersionNotSupported")
// Error in HTTP communication
ErrorTransport = ErrorType("transport")
// HTTPS required