diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml index 997e39d0..2fad73e8 100644 --- a/.github/actions/build/action.yml +++ b/.github/actions/build/action.yml @@ -17,9 +17,9 @@ runs: using: composite steps: - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: - go-version: ^1.16 + go-version: ^1.18 - name: Determine artifact output filename id: artifact-name-generator diff --git a/.github/workflows/status-checks.yml b/.github/workflows/status-checks.yml index f2b83960..79ccb521 100644 --- a/.github/workflows/status-checks.yml +++ b/.github/workflows/status-checks.yml @@ -52,9 +52,9 @@ jobs: - uses: actions/checkout@v3 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: - go-version: ^1.16 + go-version: ^1.18 - name: Run gofmt # gofmt does not return non-zero exit codes on failure, so we have to check that there are no issues using grep. @@ -75,6 +75,12 @@ jobs: - name: Run misspell run: misspell -error . + - name: Install staticcheck + run: go install honnef.co/go/tools/cmd/staticcheck@2023.1.3 + + - name: Run staticcheck + run: staticcheck -checks "all,-ST1000,-ST1003,-SA1019,-SA1029" ./... + test: runs-on: ubuntu-latest timeout-minutes: 10 diff --git a/CHANGELOG.md b/CHANGELOG.md index 7825e502..753e6247 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Validate revocation witness before revocation update is applied - RevocationStorage's EnableRevocation function does not return an error anymore if it has been enabled already - Use a scratch Docker image as base for the Dockerfile +- Custom WrapErrorPrefix function that respects the error's type +- Log info message of irma.SessionError errors As part of e-mail address revalidation: - `VerifyMXRecord` incorporates a check to see if there is an active network connection @@ -33,6 +35,16 @@ As part of e-mail address revalidation: ### Removed - Superfluous openssl package in Dockerfile +### Security +- Let IRMA servers by default reject IRMA/Yivi apps that don't support pairing codes (IRMA protocol version <= 2.7) + +**Note:** This is an important security update for issuers to make sure that pairing codes cannot be circumvented. +IRMA apps that don't support pairing codes should not be in circulation anymore, so this change won't affect users. +Yivi apps have always supported pairing codes. + +### Internal +- Linter switch from golint to staticcheck + ## [0.12.6] - 2023-05-31 ### Fixed - Legacy endpoints of keyshare server return 403 status codes when database is down diff --git a/attributes.go b/attributes.go index bed9c4ac..14465f4a 100644 --- a/attributes.go +++ b/attributes.go @@ -29,13 +29,13 @@ var ( credentialID = metadataField{16, 8} ) -// metadataField contains the length and offset of a field within a metadata attribute. +// MetadataField contains the length and offset of a field within a metadata attribute. type metadataField struct { length int offset int } -// metadataAttribute represents a metadata attribute. Contains the credential type, signing date, validity, and the public key counter. +// MetadataAttribute represents a metadata attribute. Contains the credential type, signing date, validity, and the public key counter. type MetadataAttribute struct { Int *big.Int pk *gabikeys.PublicKey @@ -59,16 +59,16 @@ type AttributeList struct { func NewAttributeListFromInts(ints []*big.Int, conf *Configuration) *AttributeList { metadata := MetadataFromInt(ints[0], conf) credtype := metadata.CredentialType() - idx := credtype.RevocationIndex + 1 - var rev bool + revocationSupported := false if credtype != nil { - rev = credtype.RevocationSupported() && + idx := credtype.RevocationIndex + 1 + revocationSupported = credtype.RevocationSupported() && len(ints) > idx && ints[idx] != nil && ints[idx].Cmp(bigZero) != 0 } return &AttributeList{ Ints: ints, MetadataAttribute: metadata, - RevocationSupported: rev, + RevocationSupported: revocationSupported, } } @@ -217,6 +217,26 @@ func (al *AttributeList) Attribute(identifier AttributeTypeIdentifier) Translate return nil } +func (al *AttributeList) CredentialInfo() *CredentialInfo { + credtype := al.CredentialType() + if credtype == nil { + return nil + } + id := credtype.Identifier() + issid := id.IssuerIdentifier() + return &CredentialInfo{ + ID: id.Name(), + IssuerID: issid.Name(), + SchemeManagerID: issid.SchemeManagerIdentifier().Name(), + SignedOn: Timestamp(al.SigningDate()), + Expires: Timestamp(al.Expiry()), + Attributes: al.Map(), + Hash: al.Hash(), + Revoked: al.Revoked, + RevocationSupported: al.RevocationSupported, + } +} + // MetadataFromInt wraps the given Int func MetadataFromInt(i *big.Int, conf *Configuration) *MetadataAttribute { return &MetadataAttribute{Int: i, Conf: conf} diff --git a/credinfo.go b/credinfo.go index c1668bba..baf85609 100644 --- a/credinfo.go +++ b/credinfo.go @@ -22,31 +22,11 @@ type CredentialInfo struct { // A CredentialInfoList is a list of credentials (implements sort.Interface). type CredentialInfoList []*CredentialInfo -func (attrs *AttributeList) CredentialInfo() *CredentialInfo { - credtype := attrs.CredentialType() - if credtype == nil { - return nil - } - id := credtype.Identifier() - issid := id.IssuerIdentifier() - return &CredentialInfo{ - 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, - } -} - func (ci CredentialInfo) GetCredentialType(conf *Configuration) *CredentialType { return conf.CredentialTypes[ci.Identifier()] } -// Returns true if credential is expired at moment of calling this function +// IsExpired returns true if credential is expired at moment of calling this function func (ci CredentialInfo) IsExpired() bool { return ci.Expires.Before(Timestamp(time.Now())) } diff --git a/descriptions.go b/descriptions.go index 9d1740a0..f067f773 100644 --- a/descriptions.go +++ b/descriptions.go @@ -101,7 +101,7 @@ type AttributeType struct { Name TranslatedString Description TranslatedString - RandomBlind bool `xml:"randomblind,attr,optional" json:",omitempty"` + RandomBlind bool `xml:"randomblind,attr,omitempty" json:",omitempty"` Index int `xml:"-"` DisplayIndex *int `xml:"displayIndex,attr" json:",omitempty"` @@ -621,7 +621,7 @@ func (ad AttributeType) IsOptional() bool { return ad.Optional == "true" } -// Returns indices of random blind attributes within this credentialtype +// RandomBlindAttributeIndices returns indices of random blind attributes within this credentialtype // The indices coincide with indices of an AttributeList (metadataAttribute at index 0) func (ct *CredentialType) RandomBlindAttributeIndices() []int { indices := []int{} diff --git a/identifiers.go b/identifiers.go index b8d81ef5..e3cd66c2 100644 --- a/identifiers.go +++ b/identifiers.go @@ -129,8 +129,8 @@ func (oi metaObjectIdentifier) Root() string { } } -func (io metaObjectIdentifier) PartsCount() int { - return strings.Count(string(io), ".") +func (oi metaObjectIdentifier) PartsCount() int { + return strings.Count(string(oi), ".") } // NewRequestorSchemeIdentifier converts the specified identifier to a RequestorSchemeIdentifier. diff --git a/internal/keysharecore/usersecrets.go b/internal/keysharecore/usersecrets.go index 72899d72..c555efd8 100644 --- a/internal/keysharecore/usersecrets.go +++ b/internal/keysharecore/usersecrets.go @@ -70,12 +70,12 @@ func (s *unencryptedUserSecrets) setID(id []byte) error { return nil } -func (user *unencryptedUserSecrets) verifyPin(pin string) error { +func (s *unencryptedUserSecrets) verifyPin(pin string) error { paddedPin, err := padBytes([]byte(pin), 64) if err != nil { return err } - if subtle.ConstantTimeCompare(user.Pin, paddedPin) != 1 { + if subtle.ConstantTimeCompare(s.Pin, paddedPin) != 1 { return ErrInvalidPin } return nil diff --git a/internal/sessiontest/legacy_test.go b/internal/sessiontest/legacy_test.go index 989423ee..fb1a045a 100644 --- a/internal/sessiontest/legacy_test.go +++ b/internal/sessiontest/legacy_test.go @@ -5,6 +5,7 @@ import ( irma "github.com/privacybydesign/irmago" "github.com/privacybydesign/irmago/internal/test" + "github.com/privacybydesign/irmago/server/irmaserver" "github.com/stretchr/testify/require" ) @@ -37,13 +38,16 @@ func testSessionUsingLegacyStorage(t *testing.T, dir string) { // Re-open client require.NoError(t, client.Close()) - client, handler = parseExistingStorage(t, handler.storage) + client, _ = parseExistingStorage(t, handler.storage) // Test whether credential is still there after the storage has been reloaded doSession(t, getDisclosureRequest(idRoot), client, nil, nil, nil, nil) } func TestWithoutPairingSupport(t *testing.T) { + irmaserver.AcceptInsecureProtocolVersions = true + defer func() { irmaserver.AcceptInsecureProtocolVersions = false }() + t.Run("SigningSession", apply(testSigningSession, nil, optionPrePairingClient)) t.Run("DisclosureSession", apply(testDisclosureSession, nil, optionPrePairingClient)) t.Run("NoAttributeDisclosureSession", apply(testNoAttributeDisclosureSession, nil, optionPrePairingClient)) diff --git a/internal/sessiontest/logs_test.go b/internal/sessiontest/logs_test.go index 066a440d..e0e82b97 100644 --- a/internal/sessiontest/logs_test.go +++ b/internal/sessiontest/logs_test.go @@ -84,7 +84,7 @@ func TestLogging(t *testing.T) { // Check whether log entry for signature session is actually stored require.NoError(t, client.Close()) - client, handler = parseExistingStorage(t, handler.storage) + client, _ = parseExistingStorage(t, handler.storage) logs, err = client.LoadNewestLogs(100) require.NoError(t, err) require.True(t, len(logs) == oldLogLength+3) diff --git a/internal/sessiontest/redis_test.go b/internal/sessiontest/redis_test.go index c27e9fdc..bc09544b 100644 --- a/internal/sessiontest/redis_test.go +++ b/internal/sessiontest/redis_test.go @@ -211,8 +211,9 @@ func TestRedisUpdates(t *testing.T) { var o interface{} transport := irma.NewHTTPTransport(qr.URL, false) - transport.SetHeader(irma.MinVersionHeader, "2.5") - transport.SetHeader(irma.MaxVersionHeader, "2.5") + transport.SetHeader(irma.MinVersionHeader, "2.8") + transport.SetHeader(irma.MaxVersionHeader, "2.8") + transport.SetHeader(irma.AuthorizationHeader, "testauthtoken") clientToken, err := mr.Get("token:" + string(token)) require.NoError(t, err) diff --git a/internal/sessiontest/session_test.go b/internal/sessiontest/session_test.go index affd3aee..d4b020a9 100644 --- a/internal/sessiontest/session_test.go +++ b/internal/sessiontest/session_test.go @@ -166,7 +166,7 @@ func testIssuanceSameAttributesNotSingleton(t *testing.T, conf interface{}, opts // Also check whether this is actually stored require.NoError(t, client.Close()) - client, handler = parseExistingStorage(t, handler.storage) + client, _ = parseExistingStorage(t, handler.storage) require.Equal(t, prevLen+1, len(client.CredentialInfoList())) } @@ -272,7 +272,7 @@ func testIssuanceSingletonCredential(t *testing.T, conf interface{}, opts ...opt // Also check whether this is actually stored require.NoError(t, client.Close()) - client, handler = parseExistingStorage(t, handler.storage) + client, _ = parseExistingStorage(t, handler.storage) require.NotNil(t, client.Attributes(credid, 0)) require.Nil(t, client.Attributes(credid, 1)) } @@ -447,7 +447,7 @@ func testIssuedCredentialIsStored(t *testing.T, conf interface{}, opts ...option doSession(t, issuanceRequest, client, nil, nil, nil, conf, opts...) require.NoError(t, client.Close()) - client, handler = parseExistingStorage(t, handler.storage) + client, _ = parseExistingStorage(t, handler.storage) id := irma.NewAttributeTypeIdentifier("irma-demo.MijnOverheid.fullName.familyname") doSession(t, getDisclosureRequest(id), client, nil, nil, nil, conf, opts...) } @@ -962,7 +962,7 @@ func TestPOSTSizeLimit(t *testing.T) { req, err := http.NewRequest( http.MethodPost, requestorServerURL+"/session/", - bytes.NewReader(make([]byte, server.PostSizeLimit+1, server.PostSizeLimit+1)), + bytes.NewReader(make([]byte, server.PostSizeLimit+1)), ) require.NoError(t, err) req.Header.Set("Content-Type", "application/json; charset=UTF-8") @@ -1091,12 +1091,36 @@ func TestDoubleGET(t *testing.T) { // Simulate the first GET by the client in the session protocol, twice var o interface{} transport := irma.NewHTTPTransport(qr.URL, false) - transport.SetHeader(irma.MinVersionHeader, "2.5") - transport.SetHeader(irma.MaxVersionHeader, "2.5") + transport.SetHeader(irma.MinVersionHeader, "2.8") + transport.SetHeader(irma.MaxVersionHeader, "2.8") + transport.SetHeader(irma.AuthorizationHeader, "testauthtoken") require.NoError(t, transport.Get("", &o)) require.NoError(t, transport.Get("", &o)) } +func TestInsecureProtocolVersion(t *testing.T) { + irmaServer := StartIrmaServer(t, nil) + defer irmaServer.Stop() + + // Test whether the server accepts a request with an insecure protocol version + request := irma.NewDisclosureRequest(irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")) + + qr, _, _, err := irmaServer.irma.StartSession(request, func(result *server.SessionResult) {}) + require.NoError(t, err) + + var o interface{} + transport := irma.NewHTTPTransport(qr.URL, false) + transport.SetHeader(irma.MinVersionHeader, "2.7") + transport.SetHeader(irma.MaxVersionHeader, "2.7") + transport.SetHeader(irma.AuthorizationHeader, "testauthtoken") + err = transport.Get("", &o) + require.Error(t, err) + serr, ok := err.(*irma.SessionError) + require.True(t, ok) + require.Equal(t, server.ErrorProtocolVersion.Status, serr.RemoteStatus) + require.Equal(t, string(server.ErrorProtocolVersion.Type), serr.RemoteError.ErrorName) +} + func TestClientDeveloperMode(t *testing.T) { common.ForceHTTPS = true defer func() { common.ForceHTTPS = false }() diff --git a/irma/cmd/genkeypair.go b/irma/cmd/genkeypair.go index c2132834..125bd28c 100644 --- a/irma/cmd/genkeypair.go +++ b/irma/cmd/genkeypair.go @@ -116,7 +116,7 @@ var genkeypairCmd = &cobra.Command{ // Now generate the key pair sysParams, ok := gabikeys.DefaultSystemParameters[keylength] if !ok { - return fmt.Errorf("Unsupported key length, should be one of %v", gabikeys.DefaultKeyLengths) + return fmt.Errorf("unsupported key length, should be one of %v", gabikeys.DefaultKeyLengths) } privk, pubk, err := gabikeys.GenerateKeyPair(sysParams, numAttributes, counter, expiryDate) if err != nil { diff --git a/irma/cmd/issuer-keyprove.go b/irma/cmd/issuer-keyprove.go index 4cc01dfa..2717b227 100644 --- a/irma/cmd/issuer-keyprove.go +++ b/irma/cmd/issuer-keyprove.go @@ -119,7 +119,7 @@ may be used.`, }() // Build the proof - bases := append([]*big.Int{pk.Z, pk.S}) + bases := []*big.Int{pk.Z, pk.S} if pk.G != nil { bases = append(bases, pk.G) } diff --git a/irma/cmd/issuer-keyverify.go b/irma/cmd/issuer-keyverify.go index cd13ad77..04ed7085 100644 --- a/irma/cmd/issuer-keyverify.go +++ b/irma/cmd/issuer-keyverify.go @@ -102,7 +102,7 @@ On machines of 2 - 3 GHz verification will take some 5 - 15 minutes, during whic follower.StepDone() // Construct proof structure - bases := append([]*big.Int{pk.Z, pk.S}) + bases := []*big.Int{pk.Z, pk.S} if pk.G != nil { bases = append(bases, pk.G) } diff --git a/irma/cmd/request.go b/irma/cmd/request.go index 65845368..6e485b70 100644 --- a/irma/cmd/request.go +++ b/irma/cmd/request.go @@ -310,7 +310,6 @@ func authmethodAlias(f *pflag.FlagSet, name string) pflag.NormalizedName { switch name { case "authmethod": name = "auth-method" - break } return pflag.NormalizedName(name) } diff --git a/irma/cmd/sign.go b/irma/cmd/sign.go index 156b29b6..6ff58052 100644 --- a/irma/cmd/sign.go +++ b/irma/cmd/sign.go @@ -184,7 +184,7 @@ func skipSigning(path string, info os.FileInfo, typ irma.SchemeType) bool { } if !strings.HasSuffix(path, ".xml") && !strings.HasSuffix(path, ".png") && - !regexp.MustCompile("kss-\\d+\\.pem$").Match([]byte(filepath.Base(path))) && + !regexp.MustCompile(`kss-\d+\.pem$`).Match([]byte(filepath.Base(path))) && filepath.Base(path) != "timestamp" { return true } diff --git a/irmaclient/client.go b/irmaclient/client.go index e9a5cf84..75ae1699 100644 --- a/irmaclient/client.go +++ b/irmaclient/client.go @@ -16,7 +16,6 @@ import ( irma "github.com/privacybydesign/irmago" "github.com/privacybydesign/irmago/internal/common" "github.com/privacybydesign/irmago/internal/concmap" - "github.com/sirupsen/logrus" ) // This file contains most methods of the Client (c.f. session.go @@ -75,6 +74,7 @@ type Client struct { credMutex sync.Mutex } +// Preferences contains the preferences of the user of this client. // TODO: consider if we should save irmamobile preferences here, because they would automatically // be part of any backup and syncing solution we implement at a later time type Preferences struct { @@ -252,15 +252,6 @@ func (client *Client) loadCredentialStorage() (err error) { return } -func (client *Client) nonrevCredPrepareCache(credid irma.CredentialTypeIdentifier, index int) error { - irma.Logger.WithFields(logrus.Fields{"credid": credid, "index": index}).Debug("Preparing cache") - cred, err := client.credential(credid, index) - if err != nil { - return err - } - return cred.NonrevPrepareCache() -} - func (client *Client) reportError(err error) { irma.Logger.Error(err) client.handler.ReportError(err) @@ -452,8 +443,8 @@ func (client *Client) RemoveCredentialByHash(hash string) error { return client.RemoveCredential(cred.CredentialType().Identifier(), index) } -// Removes all attributes, signatures, logs and userdata -// Includes the user's secret key, keyshare servers and preferences/updates +// RemoveStorage removes all attributes, signatures, logs and userdata. +// This includes the user's secret key, keyshare servers and preferences/updates. // A fresh secret key is installed. func (client *Client) RemoveStorage() error { var err error @@ -1122,7 +1113,7 @@ func (client *Client) keyshareEnrollWorker(managerID irma.SchemeManagerIdentifie } } if err != nil { - return errors.WrapPrefix(err, "failed to validate pin", 0) + return irma.WrapErrorPrefix(err, "failed to validate pin") } if !pinCorrect { return errors.New("incorrect pin") @@ -1449,7 +1440,7 @@ func (client *Client) ConfigurationUpdated(downloaded *irma.IrmaIdentifierSet) e if diff <= 0 { continue } - attrs = append(attrs, make([]*big.Int, diff, diff)...) + attrs = append(attrs, make([]*big.Int, diff)...) for j := len(attrs) - diff; j < len(attrs); j++ { attrs[j] = big.NewInt(0) } diff --git a/irmaclient/irmaclient_test.go b/irmaclient/irmaclient_test.go index f9492230..0ddc509a 100644 --- a/irmaclient/irmaclient_test.go +++ b/irmaclient/irmaclient_test.go @@ -292,7 +292,7 @@ func TestCredentialRemoval(t *testing.T) { // Also check whether credential is removed after reloading the storage err = client.storage.db.Close() require.NoError(t, err) - client, handler = parseExistingStorage(t, handler.storage) + client, _ = parseExistingStorage(t, handler.storage) cred, err = client.credential(id2, 0) require.NoError(t, err) require.Nil(t, cred) @@ -359,7 +359,7 @@ func TestKeyshareEnrollmentRemoval(t *testing.T) { err = client.storage.db.Close() require.NoError(t, err) - client, handler = parseExistingStorage(t, handler.storage) + client, _ = parseExistingStorage(t, handler.storage) require.NotContains(t, client.keyshareServers, "test") } diff --git a/irmaclient/keyshare.go b/irmaclient/keyshare.go index 8ea83051..08097474 100644 --- a/irmaclient/keyshare.go +++ b/irmaclient/keyshare.go @@ -81,8 +81,8 @@ func newKeyshareServer(schemeManagerIdentifier irma.SchemeManagerIdentifier) (*k return ks, nil } -func (ks *keyshareServer) HashedPin(pin string) string { - hash := sha256.Sum256(append(ks.Nonce, []byte(pin)...)) +func (kss *keyshareServer) HashedPin(pin string) string { + hash := sha256.Sum256(append(kss.Nonce, []byte(pin)...)) // We must be compatible with the old Android app here, // which uses Base64.encodeToString(hash, Base64.DEFAULT), // which appends a newline. @@ -185,30 +185,6 @@ func startKeyshareSession( } } -func (ks *keyshareSession) fail(manager irma.SchemeManagerIdentifier, err error) { - serr, ok := err.(*irma.SessionError) - if ok { - if serr.RemoteError != nil && len(serr.RemoteError.ErrorName) > 0 { - switch serr.RemoteError.ErrorName { - case "USER_NOT_FOUND": - ks.sessionHandler.KeyshareEnrollmentDeleted(manager) - case "USER_NOT_REGISTERED": - ks.sessionHandler.KeyshareEnrollmentIncomplete(manager) - case "USER_BLOCKED": - duration, err := strconv.Atoi(serr.RemoteError.Message) - if err != nil { // Not really clear what to do with duration, but should never happen anyway - duration = -1 - } - ks.sessionHandler.KeyshareBlocked(manager, duration) - default: - ks.sessionHandler.KeyshareError(&manager, err) - } - } - } else { - ks.sessionHandler.KeyshareError(&manager, err) - } -} - // Ask for a pin, repeatedly if necessary, and either continue the keyshare protocol // with authorization, or stop the keyshare protocol and inform of failure. func (ks *keyshareSession) VerifyPin(attempts int) { diff --git a/irmaclient/legacy.go b/irmaclient/legacy.go index 83bbc025..31b1da38 100644 --- a/irmaclient/legacy.go +++ b/irmaclient/legacy.go @@ -535,14 +535,14 @@ func (kss *keyshareServer) registerPublicKey(client *Client, transport *irma.HTT }, }) if err != nil { - err = errors.WrapPrefix(err, "failed to sign public key registration JWT", 0) + err = irma.WrapErrorPrefix(err, "failed to sign public key registration JWT") return nil, err } result := &irma.KeysharePinStatus{} err = transport.Post("users/register_publickey", result, irma.KeyshareKeyRegistration{PublicKeyRegistrationJWT: jwtt}) if err != nil { - err = errors.WrapPrefix(err, "failed to register public key", 0) + err = irma.WrapErrorPrefix(err, "failed to register public key") return nil, err } @@ -551,7 +551,7 @@ func (kss *keyshareServer) registerPublicKey(client *Client, transport *irma.HTT kss.ChallengeResponse = true err = client.storage.StoreKeyshareServers(client.keyshareServers) if err != nil { - err = errors.WrapPrefix(err, "failed to store updated keyshare server", 0) + err = irma.WrapErrorPrefix(err, "failed to store updated keyshare server") return nil, err } } diff --git a/irmaclient/revocation.go b/irmaclient/revocation.go index f7c9f4ee..a97ffdb7 100644 --- a/irmaclient/revocation.go +++ b/irmaclient/revocation.go @@ -68,7 +68,7 @@ func (client *Client) initRevocation() { irma.Logger.WithFields(logrus.Fields{ "random": r, "prob": p, - "lastupdated": time.Now().Sub(cred.NonRevocationWitness.Updated).Seconds(), + "lastupdated": time.Since(cred.NonRevocationWitness.Updated).Seconds(), "credtype": id, "hash": attrs.Hash(), }).Debug("scheduling nonrevocation witness remote update") @@ -277,7 +277,7 @@ func (cred *credential) nonrevApplyUpdates(update *revocation.Update, keys irma. // This should never happen, but it makes debugging easier if something goes wrong. if cred.NonRevocationWitness != nil { if err := cred.NonRevocationWitness.Verify(cred.Pk); err != nil { - return false, errors.WrapPrefix(err, "revocation witness is in an inconsistent state", 0) + return false, irma.WrapErrorPrefix(err, "revocation witness is in an inconsistent state") } } @@ -300,7 +300,7 @@ func probability(lastUpdate time.Time, refindex uint64) float64 { refprobability = 0.75 * asymptote // probability after one week ) f := math.Tan(math.Pi * refprobability / (2 * asymptote)) - i := time.Now().Sub(lastUpdate).Seconds() + i := time.Since(lastUpdate).Seconds() return 2 * asymptote / math.Pi * math.Atan(i/float64(refindex)*f) } diff --git a/irmaclient/session.go b/irmaclient/session.go index d7dbedae..eeeac9b6 100644 --- a/irmaclient/session.go +++ b/irmaclient/session.go @@ -369,8 +369,8 @@ func (session *session) processSessionInfo() { issuedAt := time.Now() _, err := ir.GetCredentialInfoList(session.client.Configuration, session.Version, issuedAt) if err != nil { - if err, ok := err.(*irma.SessionError); ok { - session.fail(err) + if serr, ok := err.(*irma.SessionError); ok { + session.fail(serr) } else { session.fail(&irma.SessionError{ErrorType: irma.ErrorUnknownIdentifier, Err: err}) } diff --git a/irmaclient/signer.go b/irmaclient/signer.go index 926358d7..581c069c 100644 --- a/irmaclient/signer.go +++ b/irmaclient/signer.go @@ -21,7 +21,7 @@ type Signer interface { // encoding used for JWTs (in which the bytes of r and s are concatenated after each other in one // byte slice). func signatureJwtEncoding(signature []byte) ([]byte, error) { - ints := make([]*gobig.Int, 2, 2) + ints := make([]*gobig.Int, 2) _, err := asn1.Unmarshal(signature, &ints) if err != nil { return nil, err diff --git a/irmaconfig.go b/irmaconfig.go index daf05f54..0f40212f 100644 --- a/irmaconfig.go +++ b/irmaconfig.go @@ -67,7 +67,7 @@ type Configuration struct { readOnly bool } -// ConfigurationListeners are the interface provided to react to changes in schemes. +// ConfigurationListener are the interface provided to react to changes in schemes. type ConfigurationListener func(conf *Configuration) type UnknownIdentifierError struct { @@ -101,7 +101,7 @@ func NewConfiguration(path string, opts ConfigurationOptions) (conf *Configurati if conf.assets != "" { // If an assets folder is specified, then it must exist if err = common.AssertPathExists(conf.assets); err != nil { - return nil, errors.WrapPrefix(err, "Nonexistent assets folder specified", 0) + return nil, WrapErrorPrefix(err, "Nonexistent assets folder specified") } } if err = common.EnsureDirectoryExists(conf.Path); err != nil { @@ -596,8 +596,6 @@ func (conf *Configuration) checkCredentialTypes(session SessionRequest, missing } return nil }) - - return } func (conf *Configuration) checkIdentifiers(session SessionRequest) (*IrmaIdentifierSet, *IrmaIdentifierSet, error) { diff --git a/irmago_test.go b/irmago_test.go index 06ef13f9..c8a12086 100644 --- a/irmago_test.go +++ b/irmago_test.go @@ -874,14 +874,6 @@ func credidptr(s string) *CredentialTypeIdentifier { id := credid(s) return &id } -func credinfo(id string) *CredentialInfo { - i := credid(id) - return &CredentialInfo{ - SchemeManagerID: i.Root(), - IssuerID: i.IssuerIdentifier().Name(), - ID: i.Name(), - } -} func credtype(id string, deps ...string) *CredentialType { i := credid(id) d := CredentialDependencies{{{}}} diff --git a/keyring.go b/keyring.go index 9e996725..d563b283 100644 --- a/keyring.go +++ b/keyring.go @@ -81,7 +81,7 @@ func NewPrivateKeyRingFolder(path string, conf *Configuration) (*PrivateKeyRingF return ring, nil } -func (_ *PrivateKeyRingFolder) parseFilename(filename string) (*IssuerIdentifier, *uint, error) { +func (*PrivateKeyRingFolder) parseFilename(filename string) (*IssuerIdentifier, *uint, error) { // This regexp returns one of the following: // [ "foo.bar.xml", "foo.bar", "", "" ] in case of "foo.bar.xml" // [ "foo.bar.xml", "foo.bar", ".2", "2" ] in case of "foo.bar.2.xml" diff --git a/messages.go b/messages.go index f2251c6f..76714434 100644 --- a/messages.go +++ b/messages.go @@ -64,7 +64,7 @@ func (v *ProtocolVersion) MarshalJSON() ([]byte, error) { return json.Marshal(v.String()) } -// Returns true if v is below the given version. +// Below returns true if v is below the given version. func (v *ProtocolVersion) Below(major, minor int) bool { if v.Major < major { return true @@ -72,10 +72,12 @@ func (v *ProtocolVersion) Below(major, minor int) bool { return v.Major == major && v.Minor < minor } +// BelowVersion returns true if v is below the given version. func (v *ProtocolVersion) BelowVersion(other *ProtocolVersion) bool { return v.Below(other.Major, other.Minor) } +// Above returns true if v is above the given version. func (v *ProtocolVersion) Above(major, minor int) bool { if v.Major > major { return true @@ -83,6 +85,7 @@ func (v *ProtocolVersion) Above(major, minor int) bool { return v.Major == major && v.Minor > minor } +// AboveVersion returns true if v is above the given version. func (v *ProtocolVersion) AboveVersion(other *ProtocolVersion) bool { return v.Above(other.Major, other.Minor) } @@ -171,8 +174,10 @@ type Qr struct { Type Action `json:"irmaqr"` } -// Tokens to identify a session from the perspective of the different agents +// RequestorToken identifies a session from the perspective of the requestor. type RequestorToken string + +// ClientToken identifies a session from the perspective of the client. type ClientToken string // ParseClientToken parses a string to a ClientToken after validating the input. @@ -194,8 +199,10 @@ func ParseRequestorToken(input string) (RequestorToken, error) { } // Authorization headers -type ClientAuthorization string -type FrontendAuthorization string +type ( + ClientAuthorization string + FrontendAuthorization string +) // Client statuses const ( @@ -408,6 +415,10 @@ func (e *SessionError) Error() string { buffer.WriteString("Error type: ") buffer.WriteString(string(typ)) + if len(e.Info) > 0 { + buffer.WriteString("\nInfo: ") + buffer.WriteString(e.Info) + } if e.Err != nil { buffer.WriteString("\nDescription: ") buffer.WriteString(e.Err.Error()) @@ -417,7 +428,7 @@ func (e *SessionError) Error() string { buffer.WriteString(strconv.Itoa(e.RemoteStatus)) } if e.RemoteError != nil { - buffer.WriteString("\nIRMA server error: ") + buffer.WriteString("\nServer error: ") buffer.WriteString(e.RemoteError.Error()) } @@ -465,7 +476,7 @@ func ParseRequestorJwt(action string, requestorJwt string) (RequestorJwt, error) return nil, err } if err := retval.RequestorRequest().Validate(); err != nil { - return nil, errors.WrapPrefix(err, "Invalid JWT body", 0) + return nil, WrapErrorPrefix(err, "Invalid JWT body") } return retval, nil } @@ -513,3 +524,18 @@ type FrontendSessionStatus struct { Status ServerStatus `json:"status"` NextSession *Qr `json:"nextSession,omitempty"` } + +func WrapErrorPrefix(err error, msg string) error { + // If error is already a SessionError, just add the prefix to the info + if sessionErr, ok := err.(*SessionError); ok { + return &SessionError{ + Err: sessionErr.Err, + ErrorType: sessionErr.ErrorType, + Info: fmt.Sprintf("%s: %s", msg, sessionErr.Info), + RemoteError: sessionErr.RemoteError, + } + } + + // Otherwise just use error.WrapPrefix + return errors.WrapPrefix(err, msg, 0) +} diff --git a/requests.go b/requests.go index 7d20f331..2ccf5828 100644 --- a/requests.go +++ b/requests.go @@ -674,7 +674,7 @@ func stringSliceEqual(a, b []string) bool { if len(a) != len(b) { return false } - for i, _ := range a { + for i := range a { if a[i] != b[i] { return false } @@ -747,7 +747,7 @@ func (ir *IssuanceRequest) Identifiers() *IrmaIdentifierSet { ir.ids.Issuers[issuer] = struct{}{} credID := credreq.CredentialTypeID ir.ids.CredentialTypes[credID] = struct{}{} - for attr, _ := range credreq.Attributes { // this is kind of ugly + for attr := range credreq.Attributes { // this is kind of ugly ir.ids.AttributeTypes[NewAttributeTypeIdentifier(credID.String()+"."+attr)] = struct{}{} } if ir.ids.PublicKeys[issuer] == nil { @@ -858,20 +858,22 @@ func (sr *SignatureRequest) Validate() error { return nil } -// Check if Timestamp is before other Timestamp. Used for checking expiry of attributes +// Before checks 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)) } +// After checks if Timestamp is after other Timestamp. Used for checking expiry of attributes. func (t Timestamp) After(u Timestamp) bool { return time.Time(t).After(time.Time(u)) } +// Sub returns the time difference between two Timestamps. func (t Timestamp) Sub(u Timestamp) time.Duration { return time.Time(t).Sub(time.Time(u)) } -// To check whether Timestamp is uninitialized +// IsZero checks whether Timestamp is uninitialized func (t Timestamp) IsZero() bool { return time.Time(t).IsZero() } @@ -904,7 +906,7 @@ func (t *Timestamp) UnmarshalJSON(b []byte) error { return nil } -// Timestamp implements Stringer. +// String returns the timestamp as a Unix time string. func (t *Timestamp) String() string { return fmt.Sprint(time.Time(*t).Unix()) } diff --git a/revocation.go b/revocation.go index 4b24dff0..9f32cd36 100644 --- a/revocation.go +++ b/revocation.go @@ -720,7 +720,7 @@ func (rs *RevocationStorage) SetRevocationUpdates(b *BaseRequest) error { Logger.WithError(err).Warnf( "failed to fetch revocation updates for %s, nonrevocation is guaranteed only until %s ago", credid, - time.Now().Sub(updated).String(), + time.Since(updated).String(), ) } else { Logger.WithError(err).Errorf("revocation is disabled for %s: failed to fetch revocation updates and none are known locally", credid) diff --git a/schemes.go b/schemes.go index 916b099e..d226e28a 100644 --- a/schemes.go +++ b/schemes.go @@ -62,7 +62,7 @@ type ( path() string setPath(path string) parseContents(conf *Configuration) error - validate(conf *Configuration) (error, SchemeManagerStatus) + validate(conf *Configuration) (SchemeManagerStatus, error) update() error handleUpdateFile(conf *Configuration, path, filename string, bts []byte, transport *HTTPTransport, _ *IrmaIdentifierSet) error delete(conf *Configuration) error @@ -298,7 +298,7 @@ func (conf *Configuration) ParseSchemeFolder(dir string) (scheme Scheme, serr er }() // validate scheme contents - if err, status := scheme.validate(conf); err != nil { + if status, err := scheme.validate(conf); err != nil { serr = &SchemeManagerError{Scheme: id, Status: status, Err: err} return } @@ -362,7 +362,7 @@ func (conf *Configuration) parseSchemeDescription(dir string) (Scheme, SchemeMan return nil, SchemeManagerStatusParsingError, err } - index, err, _ := conf.parseIndex(dir) + index, _, err := conf.parseIndex(dir) if err != nil { return nil, SchemeManagerStatusParsingError, err } @@ -396,7 +396,7 @@ func (conf *Configuration) parseSchemeDescription(dir string) (Scheme, SchemeMan var ts *Timestamp ts, exists, err = readTimestamp(filepath.Join(dir, "timestamp")) if err != nil || !exists { - return scheme, SchemeManagerStatusParsingError, errors.WrapPrefix(err, "Could not read scheme manager timestamp", 0) + return scheme, SchemeManagerStatusParsingError, WrapErrorPrefix(err, "Could not read scheme manager timestamp") } scheme.setTimestamp(*ts) @@ -631,7 +631,7 @@ func (conf *Configuration) isUpToDate(subdir string) (bool, error) { } newTime, exists, err := readTimestamp(filepath.Join(conf.assets, subdir, "timestamp")) if err != nil || !exists { - return true, errors.WrapPrefix(err, "Could not read asset timestamp of scheme "+subdir, 0) + return true, WrapErrorPrefix(err, "Could not read asset timestamp of scheme "+subdir) } // The storage version of the manager does not need to have a timestamp. If it does not, it is outdated. oldTime, exists, err := readTimestamp(filepath.Join(conf.Path, subdir, "timestamp")) @@ -730,26 +730,26 @@ func (conf *Configuration) readHashedFile(path string, hash SchemeFileHash) ([]b } // parseIndex parses the index file of the specified manager. -func (conf *Configuration) parseIndex(dir string) (SchemeManagerIndex, error, SchemeManagerStatus) { +func (conf *Configuration) parseIndex(dir string) (SchemeManagerIndex, SchemeManagerStatus, error) { if err := conf.verifySignature(dir); err != nil { - return nil, err, SchemeManagerStatusInvalidSignature + return nil, SchemeManagerStatusInvalidSignature, err } path := filepath.Join(dir, "index") if err := common.AssertPathExists(path); err != nil { - return nil, fmt.Errorf("missing scheme manager index file; tried %s", path), SchemeManagerStatusInvalidIndex + return nil, SchemeManagerStatusInvalidIndex, fmt.Errorf("missing scheme manager index file; tried %s", path) } indexbts, err := os.ReadFile(path) if err != nil { - return nil, err, SchemeManagerStatusInvalidIndex + return nil, SchemeManagerStatusInvalidIndex, err } index := SchemeManagerIndex(make(map[string]SchemeFileHash)) if err = index.FromString(string(indexbts)); err != nil { - return nil, err, SchemeManagerStatusInvalidIndex + return nil, SchemeManagerStatusInvalidIndex, err } if err = conf.checkUnsignedFiles(dir, index); err != nil { - return nil, err, SchemeManagerStatusContentParsingError + return nil, SchemeManagerStatusContentParsingError, err } - return index, nil, SchemeManagerStatusValid + return index, SchemeManagerStatusValid, nil } func (conf *Configuration) checkUnsignedFiles(dir string, index SchemeManagerIndex) error { @@ -903,9 +903,9 @@ var ( regexp.MustCompile(`\.DS_Store$`), } - issPattern = regexp.MustCompile("^([^/]+)/description\\.xml") - credPattern = regexp.MustCompile("([^/]+)/Issues/([^/]+)/description\\.xml") - keyPattern = regexp.MustCompile("([^/]+)/PublicKeys/(\\d+)\\.xml") + issPattern = regexp.MustCompile(`^([^/]+)/description\.xml`) + credPattern = regexp.MustCompile(`([^/]+)/Issues/([^/]+)/description\.xml`) + keyPattern = regexp.MustCompile(`([^/]+)/PublicKeys/(\d+)\.xml`) ) func newScheme(typ SchemeType) Scheme { @@ -1038,15 +1038,6 @@ func (ct CredentialType) validateDependencies(conf *Configuration, validatedDeps return nil } -func (d DependencyChain) contains(item CredentialTypeIdentifier) bool { - for _, dep := range d { - if dep == item { - return true - } - } - return false -} - func (d DependencyChain) String() string { deps := make([]string, len(d)) for i := 0; i < len(d); i++ { @@ -1055,23 +1046,23 @@ func (d DependencyChain) String() string { return strings.Join(deps, ", ") } -func (scheme *SchemeManager) validate(conf *Configuration) (error, SchemeManagerStatus) { +func (scheme *SchemeManager) validate(conf *Configuration) (SchemeManagerStatus, error) { if scheme.XMLVersion < 7 { - return errors.New("Unsupported scheme manager description"), SchemeManagerStatusParsingError + return SchemeManagerStatusParsingError, errors.New("Unsupported scheme manager description") } if scheme.KeyshareServer != "" { if err := common.AssertPathExists(filepath.Join(scheme.path(), "kss-0.pem")); err != nil { - return errors.Errorf("Scheme %s has keyshare URL but no keyshare public key kss-0.pem", scheme.ID), SchemeManagerStatusParsingError + return SchemeManagerStatusParsingError, errors.Errorf("Scheme %s has keyshare URL but no keyshare public key kss-0.pem", scheme.ID) } } conf.validateTranslations(fmt.Sprintf("Scheme %s", scheme.ID), scheme, scheme.Languages) // Verify that all other files are validly signed if err := scheme.verifyFiles(conf); err != nil { - return err, SchemeManagerStatusInvalidSignature + return SchemeManagerStatusInvalidSignature, err } - return nil, SchemeManagerStatusValid + return SchemeManagerStatusValid, nil } func (scheme *SchemeManager) update() error { @@ -1164,7 +1155,7 @@ func (scheme *SchemeManager) present(id string, conf *Configuration) bool { return conf.SchemeManagers[NewSchemeManagerIdentifier(id)] != nil } -func (_ *SchemeManager) typ() SchemeType { return SchemeTypeIssuer } +func (*SchemeManager) typ() SchemeType { return SchemeTypeIssuer } func (scheme *SchemeManager) purge(conf *Configuration) { id := scheme.Identifier() @@ -1375,7 +1366,7 @@ func (scheme *RequestorScheme) parseContents(conf *Configuration) error { return nil } -func (scheme *RequestorScheme) validate(conf *Configuration) (error, SchemeManagerStatus) { +func (scheme *RequestorScheme) validate(conf *Configuration) (SchemeManagerStatus, error) { // Verify all files in index, reading the RequestorChunks var ( requestors []*RequestorInfo @@ -1390,14 +1381,14 @@ func (scheme *RequestorScheme) validate(conf *Configuration) (error, SchemeManag var currentChunk RequestorChunk exists, err = conf.parseSchemeFile(scheme, file[len(scheme.id())+1:], ¤tChunk) if !exists { - return errors.Errorf("file %s of requestor scheme %s in index but not found on disk", file, scheme.ID), SchemeManagerStatusParsingError + return SchemeManagerStatusParsingError, errors.Errorf("file %s of requestor scheme %s in index but not found on disk", file, scheme.ID) } if err != nil { - return err, SchemeManagerStatusParsingError + return SchemeManagerStatusParsingError, err } for _, v := range currentChunk { if v.Scheme != scheme.ID { - return errors.Errorf("Requestor %s has incorrect scheme %s", v.Name, v.Scheme), SchemeManagerStatusParsingError + return SchemeManagerStatusParsingError, errors.Errorf("Requestor %s has incorrect scheme %s", v.Name, v.Scheme) } } @@ -1410,14 +1401,14 @@ func (scheme *RequestorScheme) validate(conf *Configuration) (error, SchemeManag requestor.Languages = scheme.Languages } if scheme.Demo && len(requestor.Hostnames) > 0 { - return errors.New("Demo requestor has hostnames: only allowed for non-demo schemes"), SchemeManagerStatusParsingError + return SchemeManagerStatusParsingError, errors.New("Demo requestor has hostnames: only allowed for non-demo schemes") } if requestor.ID.RequestorSchemeIdentifier() != scheme.ID { - return errors.Errorf("requestor %s has incorrect ID", requestor.ID), SchemeManagerStatusParsingError + return SchemeManagerStatusParsingError, errors.Errorf("requestor %s has incorrect ID", requestor.ID) } if requestor.Logo != nil { - if err, status := scheme.checkLogo(conf, *requestor.Logo); err != nil { - return err, status + if status, err := scheme.checkLogo(conf, *requestor.Logo); err != nil { + return status, err } } for id, wizard := range requestor.Wizards { @@ -1425,14 +1416,14 @@ func (scheme *RequestorScheme) validate(conf *Configuration) (error, SchemeManag wizard.Languages = requestor.Languages } if id != wizard.ID || id.RequestorIdentifier() != requestor.ID { - return errors.Errorf("issue wizard %s has incorrect ID", id), SchemeManagerStatusParsingError + return SchemeManagerStatusParsingError, errors.Errorf("issue wizard %s has incorrect ID", id) } if err = wizard.Validate(conf); err != nil { - return errors.Errorf("issue wizard %s: %w", id, err), SchemeManagerStatusParsingError + return SchemeManagerStatusParsingError, errors.Errorf("issue wizard %s: %w", id, err) } if wizard.Logo != nil { - if err, status := scheme.checkLogo(conf, *wizard.Logo); err != nil { - return err, status + if status, err := scheme.checkLogo(conf, *wizard.Logo); err != nil { + return status, err } path := filepath.Join(scheme.path(), "assets", *wizard.Logo+".png") wizard.LogoPath = &path @@ -1441,19 +1432,19 @@ func (scheme *RequestorScheme) validate(conf *Configuration) (error, SchemeManag } scheme.requestors = requestors - return nil, SchemeManagerStatusValid + return SchemeManagerStatusValid, nil } -func (scheme *RequestorScheme) checkLogo(conf *Configuration, logo string) (error, SchemeManagerStatus) { +func (scheme *RequestorScheme) checkLogo(conf *Configuration, logo string) (SchemeManagerStatus, error) { var hash []byte hash, err := hex.DecodeString(logo) if err != nil { - return err, SchemeManagerStatusParsingError + return SchemeManagerStatusParsingError, err } if _, err = conf.readHashedFile(filepath.Join(scheme.path(), "assets", logo+".png"), hash); err != nil { - return err, SchemeManagerStatusInvalidSignature + return SchemeManagerStatusInvalidSignature, err } - return nil, "" + return "", nil } func (scheme *RequestorScheme) update() error { @@ -1543,7 +1534,7 @@ func (scheme *RequestorScheme) present(id string, conf *Configuration) bool { return conf.RequestorSchemes[NewRequestorSchemeIdentifier(id)] != nil } -func (_ *RequestorScheme) typ() SchemeType { return SchemeTypeRequestor } +func (*RequestorScheme) typ() SchemeType { return SchemeTypeRequestor } // purge removes a requestor scheme and its requestors from the configuration func (scheme *RequestorScheme) purge(conf *Configuration) { diff --git a/server/api.go b/server/api.go index 11d0600a..8a458af9 100644 --- a/server/api.go +++ b/server/api.go @@ -6,7 +6,6 @@ import ( "crypto/rsa" "encoding/hex" "encoding/json" - "fmt" "io" "net" "net/http" @@ -56,7 +55,8 @@ type LogOptions struct { Response, Headers, From, EncodeBinary bool } -// Remove this when dropping support for legacy pre-condiscon session requests +// LegacySessionResult is a pre-condiscon version of SessionResult. +// Remove this when dropping support for legacy pre-condiscon session requests. type LegacySessionResult struct { Token irma.RequestorToken `json:"token"` Status irma.ServerStatus `json:"status"` @@ -81,7 +81,8 @@ const ( var PostSizeLimit int64 = 10 << 20 // 10 MB -// Remove this when dropping support for legacy pre-condiscon session requests +// Legacy returns a pre-condiscon version of this SessionResult. +// Remove this when dropping support for legacy pre-condiscon session requests. func (r *SessionResult) Legacy() *LegacySessionResult { var disclosed []*irma.DisclosedAttribute for _, l := range r.Disclosed { @@ -379,7 +380,7 @@ func log(level logrus.Level, err error) error { if e, ok := err.(*errors.Error); ok && Logger.IsLevelEnabled(logrus.DebugLevel) { _, _ = writer.Write([]byte(e.ErrorStack())) } else { - _, _ = writer.Write([]byte(fmt.Sprintf("%s", err.Error()))) + _, _ = writer.Write([]byte(err.Error())) } return err } diff --git a/server/api_test.go b/server/api_test.go index 4cce1a0e..51a0261c 100644 --- a/server/api_test.go +++ b/server/api_test.go @@ -117,7 +117,7 @@ func TestServerTimeouts(t *testing.T) { defer common.Close(r.Body) // check that reading has halted with an error just after the deadline require.Error(t, err) - require.Greater(t, int64(timeout+50*time.Millisecond), int64(time.Now().Sub(start))) + require.Greater(t, int64(timeout+50*time.Millisecond), int64(time.Since(start))) w.WriteHeader(400) }), body: readerFunc(func(p []byte) (int, error) { @@ -145,7 +145,7 @@ func TestServerTimeouts(t *testing.T) { // Check whether an error is returned when the context deadline exceeds and the handler // does not act upon this within 200 ms. We add 50 ms slack to prevent race conditions. - require.Greater(t, int64(timeout+250*time.Millisecond), int64(time.Now().Sub(start))) + require.Greater(t, int64(timeout+250*time.Millisecond), int64(time.Since(start))) require.GreaterOrEqual(t, res.StatusCode, 400) require.True(t, called) }) diff --git a/server/irmaserver/handle.go b/server/irmaserver/handle.go index 9ae70f06..04f35a26 100644 --- a/server/irmaserver/handle.go +++ b/server/irmaserver/handle.go @@ -620,5 +620,4 @@ func (s *Server) handleRevocationPostIssuanceRecord(w http.ResponseWriter, r *ht server.WriteBinaryResponse(w, nil, server.RemoteError(server.ErrorRevocation, err.Error())) } w.WriteHeader(200) - return } diff --git a/server/irmaserver/helpers.go b/server/irmaserver/helpers.go index bc80e012..391c4b3f 100644 --- a/server/irmaserver/helpers.go +++ b/server/irmaserver/helpers.go @@ -129,22 +129,25 @@ func (session *session) fail(err server.Error, message string) *irma.RemoteError } func (session *session) chooseProtocolVersion(minClient, maxClient *irma.ProtocolVersion) (*irma.ProtocolVersion, error) { - // Set minimum supported version to 2.5 if condiscon compatibility is required - minServer := minProtocolVersion - if !session.LegacyCompatible { - minServer = &irma.ProtocolVersion{Major: 2, Minor: 5} - } - // Set minimum to 2.6 if nonrevocation is required - if len(session.request.Base().Revocation) > 0 { - minServer = &irma.ProtocolVersion{Major: 2, Minor: 6} - } - // Set minimum to 2.7 if chained session are used - if session.Rrequest.Base().NextSession != nil { - minServer = &irma.ProtocolVersion{Major: 2, Minor: 7} + minSessionProtocolVersion := minSecureProtocolVersion + if AcceptInsecureProtocolVersions { + // Set minimum supported version to 2.5 if condiscon compatibility is required + minSessionProtocolVersion = minProtocolVersion + if !session.LegacyCompatible { + minSessionProtocolVersion = &irma.ProtocolVersion{Major: 2, Minor: 5} + } + // Set minimum to 2.6 if nonrevocation is required + if len(session.request.Base().Revocation) > 0 { + minSessionProtocolVersion = &irma.ProtocolVersion{Major: 2, Minor: 6} + } + // Set minimum to 2.7 if chained session are used + if session.Rrequest.Base().NextSession != nil { + minSessionProtocolVersion = &irma.ProtocolVersion{Major: 2, Minor: 7} + } } - if minClient.AboveVersion(maxProtocolVersion) || maxClient.BelowVersion(minServer) || maxClient.BelowVersion(minClient) { - err := errors.Errorf("Protocol version negotiation failed, min=%s max=%s minServer=%s maxServer=%s", minClient.String(), maxClient.String(), minServer.String(), maxProtocolVersion.String()) + if minClient.AboveVersion(maxProtocolVersion) || maxClient.BelowVersion(minSessionProtocolVersion) || maxClient.BelowVersion(minClient) { + err := errors.Errorf("Protocol version negotiation failed, min=%s max=%s minServer=%s maxServer=%s", minClient.String(), maxClient.String(), minSessionProtocolVersion.String(), maxProtocolVersion.String()) _ = server.LogWarning(err) return nil, err } diff --git a/server/irmaserver/sessions.go b/server/irmaserver/sessions.go index bad5f522..4462e435 100644 --- a/server/irmaserver/sessions.go +++ b/server/irmaserver/sessions.go @@ -119,8 +119,13 @@ const ( ) var ( - minProtocolVersion = irma.NewVersion(2, 4) - maxProtocolVersion = irma.NewVersion(2, 8) + // AcceptInsecureProtocolVersions determines whether the server accepts connections from apps using an insecure protocol version. + // It is set to false by default, but can be set to true for backwards compatibility with older apps. This is not recommended. + AcceptInsecureProtocolVersions = false + + minProtocolVersion = irma.NewVersion(2, 4) + minSecureProtocolVersion = irma.NewVersion(2, 8) + maxProtocolVersion = irma.NewVersion(2, 8) minFrontendProtocolVersion = irma.NewVersion(1, 0) maxFrontendProtocolVersion = irma.NewVersion(1, 1) diff --git a/server/keyshare/keyshareserver/server.go b/server/keyshare/keyshareserver/server.go index 3d4c4c99..5450ea45 100644 --- a/server/keyshare/keyshareserver/server.go +++ b/server/keyshare/keyshareserver/server.go @@ -685,9 +685,7 @@ func (s *Server) authorizationMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Extract authorization from request authorization := r.Header.Get("Authorization") - if strings.HasPrefix(authorization, "Bearer ") { - authorization = authorization[7:] - } + authorization = strings.TrimPrefix(authorization, "Bearer ") // verify access ctx := r.Context() diff --git a/staticcheck.conf b/staticcheck.conf new file mode 100644 index 00000000..35df58f2 --- /dev/null +++ b/staticcheck.conf @@ -0,0 +1,4 @@ +checks = ["all", "-ST1000", "-ST1003", "-SA1029"] + +# In the CI, we suppress the deprecation warnings. We use the following configuration there: +# checks = ["all", "-ST1000", "-ST1003", -SA1019", "-SA1029"] diff --git a/timestamp.go b/timestamp.go index 32aadca4..4e7d9544 100644 --- a/timestamp.go +++ b/timestamp.go @@ -44,7 +44,7 @@ func TimestampRequest(message string, sigs []*big.Int, disclosed [][]*big.Int, n disclosedint := make([][]*gobig.Int, len(disclosed)) dlreps := make([]*gobig.Int, len(disclosed)) var d interface{} = disclosedint - for i, _ := range disclosed { + for i := range disclosed { meta := MetadataFromInt(disclosed[i][1], conf) if meta.CredentialType() == nil { return nil, "", errors.New("Cannot compute timestamp request involving unknown credential types") @@ -99,8 +99,8 @@ func TimestampRequest(message string, sigs []*big.Int, disclosed [][]*big.Int, n return hashed[:], timestampServerUrl, nil } -// Given an SignedMessage, verify the timestamp over the signed message, disclosed attributes, -// and rerandomized CL-signatures. +// VerifyTimestamp verifies the timestamp over the signed message, disclosed attributes, +// and rerandomized CL-signatures of the given SignedMessage. func (sm *SignedMessage) VerifyTimestamp(message string, conf *Configuration) error { // Extract the disclosed attributes and randomized CL-signatures from the proofs in order to // construct the nonce that should be signed by the timestamp server. diff --git a/verify.go b/verify.go index 1b94659e..142b44d6 100644 --- a/verify.go +++ b/verify.go @@ -15,7 +15,7 @@ import ( // ProofStatus is the status of the complete proof type ProofStatus string -// Status is the proof status of a single attribute +// AttributeProofStatus is the proof status of a single attribute type AttributeProofStatus string const ( @@ -54,9 +54,7 @@ func (pl ProofList) ExtractPublicKeys(configuration *Configuration) ([]*gabikeys var publicKeys = make([]*gabikeys.PublicKey, 0, len(pl)) for _, v := range pl { - switch v.(type) { - case *gabi.ProofD: - proof := v.(*gabi.ProofD) + if proof, ok := v.(*gabi.ProofD); ok { metadata := MetadataFromInt(proof.ADisclosed[1], configuration) // index 1 is metadata attribute publicKey, err := metadata.PublicKey() if err != nil { @@ -66,7 +64,7 @@ func (pl ProofList) ExtractPublicKeys(configuration *Configuration) ([]*gabikeys return nil, ErrMissingPublicKey } publicKeys = append(publicKeys, publicKey) - default: + } else { return nil, errors.New("Cannot extract public key, not a disclosure proofD") } } diff --git a/wait_status.go b/wait_status.go index 4b3d75d6..23c4e38f 100644 --- a/wait_status.go +++ b/wait_status.go @@ -68,7 +68,6 @@ func poll(transport *HTTPTransport, initialStatus ServerStatus, statuschan chan errorchan <- nil return } - break case err := <-errorchanPolling: if err != nil { errorchan <- err