Commit 677cb284 authored by Sietse Ringers's avatar Sietse Ringers
Browse files

Upgrade protocol



- Sessions now start with a SessionRequest instead of the requestor's JWT
- Move protocol version negotiation to later in the protocol to decouple QR contents from SessionRequest
- Fix legacy in protocol
- Support for multiple keyshare servers in issuance sessions
Co-authored-by: Tomas's avatarConfiks <confiks@scriptbase.org>
parent c1f78a8e
......@@ -49,12 +49,6 @@
packages = ["."]
revision = "720887075201f3105c335f837431426d54891b7f"
[[projects]]
branch = "master"
name = "github.com/credentials/safeprime"
packages = ["."]
revision = "424faf641b4987fde58280730486e1035e057abc"
[[projects]]
name = "github.com/davecgh/go-spew"
packages = ["spew"]
......@@ -110,10 +104,14 @@
version = "v1.0"
[[projects]]
branch = "master"
branch = "refactor"
name = "github.com/mhe/gabi"
packages = ["."]
revision = "4f733a839b2b9234ef7c2610fbc8270bafe250c7"
packages = [
".",
"safeprime"
]
revision = "30dbdc3d708aee39573ef793505e78b6fb949b48"
source = "github.com/privacybydesign/gabi"
[[projects]]
branch = "master"
......@@ -197,6 +195,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "edc6335a88ff851653a42b9198c2f0f2e7f27502e18a9b48d2641de3b0bd69be"
inputs-digest = "463d26f53ec5d07c619bf97f2707460ffa512d6eddc1f8c109972e101981afab"
solver-name = "gps-cdcl"
solver-version = 1
......@@ -38,8 +38,9 @@
version = "1.0.0"
[[constraint]]
branch = "master"
branch = "refactor"
name = "github.com/mhe/gabi"
source = "github.com/privacybydesign/gabi"
[[constraint]]
name = "github.com/pkg/errors"
......
......@@ -4,8 +4,10 @@ import (
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"fmt"
"math/big"
"strconv"
"strings"
"github.com/go-errors/errors"
"github.com/mhe/gabi"
......@@ -88,32 +90,32 @@ type publicKeyIdentifier struct {
Counter uint `json:"counter"`
}
// TODO enable this when updating protocol
//func (pki *publicKeyIdentifier) UnmarshalText(text []byte) error {
// str := string(text)
// index := strings.LastIndex(str, "-")
// if index == -1 {
// return errors.New("Invalid publicKeyIdentifier")
// }
// counter, err := strconv.Atoi(str[index+1:])
// if err != nil {
// return err
// }
// *pki = publicKeyIdentifier{Issuer: str[:index], Counter: uint(counter)}
// return nil
//}
//
//func (pki *publicKeyIdentifier) MarshalText() (text []byte, err error) {
// return []byte(fmt.Sprintf("%s-%d", pki.Issuer, pki.Counter)), nil
//}
func (pki *publicKeyIdentifier) UnmarshalText(text []byte) error {
str := string(text)
index := strings.LastIndex(str, "-")
if index == -1 {
return errors.New("Invalid publicKeyIdentifier")
}
counter, err := strconv.Atoi(str[index+1:])
if err != nil {
return err
}
*pki = publicKeyIdentifier{Issuer: str[:index], Counter: uint(counter)}
return nil
}
func (pki *publicKeyIdentifier) MarshalText() (text []byte, err error) {
return []byte(fmt.Sprintf("%s-%d", pki.Issuer, pki.Counter)), nil
}
type proofPCommitmentMap struct {
Commitments map[publicKeyIdentifier]*gabi.ProofPCommitment `json:"c"`
}
const (
kssUsernameHeader = "IRMA_Username"
kssAuthHeader = "IRMA_Authorization"
kssUsernameHeader = "X-IRMA-Keyshare-Username"
kssVersionHeader = "X-IRMA-Keyshare-ProtocolVersion"
kssAuthHeader = "Authorization"
kssAuthorized = "authorized"
kssTokenExpired = "expired"
kssPinSuccess = "success"
......@@ -196,7 +198,8 @@ func startKeyshareSession(
ks.keyshareServer = ks.keyshareServers[managerID]
transport := irma.NewHTTPTransport(ks.keyshareServer.URL)
transport.SetHeader(kssUsernameHeader, ks.keyshareServer.Username)
transport.SetHeader(kssAuthHeader, ks.keyshareServer.token)
transport.SetHeader(kssAuthHeader, "Bearer "+ks.keyshareServer.token)
transport.SetHeader(kssVersionHeader, "2")
ks.transports[managerID] = transport
authstatus := &keyshareAuthorization{}
......@@ -301,7 +304,7 @@ func (ks *keyshareSession) verifyPinAttempt(pin string) (
switch pinresult.Status {
case kssPinSuccess:
kss.token = pinresult.Message
transport.SetHeader(kssAuthHeader, kss.token)
transport.SetHeader(kssAuthHeader, "Bearer "+kss.token)
case kssPinFailure:
tries, err = strconv.Atoi(pinresult.Message)
return
......@@ -428,15 +431,10 @@ func (ks *keyshareSession) Finish(challenge *big.Int, responses map[irma.SchemeM
return
}
message := &gabi.IssueCommitmentMessage{Proofs: list, Nonce2: ks.issuerProofNonce}
for _, response := range responses {
message.ProofPjwt = response
break
message.ProofPjwts = map[string]string{}
for manager, response := range responses {
message.ProofPjwts[manager.String()] = response
}
// TODO for new protocol version
//message.ProofPjwts = map[string]string{}
//for manager, response := range responses {
// message.ProofPjwts[manager.String()] = response
//}
ks.sessionHandler.KeyshareDone(message)
}
}
......
package irmaclient
import (
"encoding/json"
"github.com/mhe/gabi"
)
// These have no (de)serializer in Java so we have to deal with how Java serializes them by default.
func (pki *publicKeyIdentifier) MarshalJSON() ([]byte, error) {
temp := struct {
Issuer map[string]string `json:"issuer"`
Counter uint `json:"counter"`
}{
Issuer: map[string]string{"identifier": pki.Issuer},
Counter: pki.Counter,
}
return json.Marshal(temp)
}
// Same as above since these use publicKeyIdentifier as map key type.
func (comms *proofPCommitmentMap) UnmarshalJSON(bytes []byte) error {
comms.Commitments = map[publicKeyIdentifier]*gabi.ProofPCommitment{}
temp := struct {
C [][]*json.RawMessage `json:"c"`
}{}
if err := json.Unmarshal(bytes, &temp); err != nil {
return err
}
for _, raw := range temp.C {
tempPkID := struct {
Issuer struct {
Identifier string `json:"identifier"`
} `json:"issuer"`
Counter uint `json:"counter"`
}{}
comm := gabi.ProofPCommitment{}
if err := json.Unmarshal([]byte(*raw[0]), &tempPkID); err != nil {
return err
}
if err := json.Unmarshal([]byte(*raw[1]), &comm); err != nil {
return err
}
pkid := publicKeyIdentifier{Issuer: tempPkID.Issuer.Identifier, Counter: tempPkID.Counter}
comms.Commitments[pkid] = &comm
}
return nil
}
......@@ -153,5 +153,3 @@ func TestManualSessionInvalidProof(t *testing.T) {
test.ClearTestStorage(t)
}
// TODO test verification without request (also with multiproof)
......@@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"net/url"
"sort"
"strings"
"math/big"
......@@ -76,8 +75,15 @@ var _ keyshareSessionHandler = (*session)(nil)
// Supported protocol versions. Minor version numbers should be reverse sorted.
var supportedVersions = map[int][]int{
2: {4, 3, 2, 1},
2: {4},
}
var minVersion = &irma.ProtocolVersion{Major: 2, Minor: supportedVersions[2][0]}
var maxVersion = &irma.ProtocolVersion{Major: 2, Minor: supportedVersions[2][len(supportedVersions[2])-1]}
const (
minVersionHeader = "X-IRMA-MinProtocolVersion"
maxVersionHeader = "X-IRMA-MaxProtocolVersion"
)
// Session constructors
......@@ -111,7 +117,7 @@ func (client *Client) newManualSession(sigrequest *irma.SignatureRequest, handle
Action: irma.ActionSigning, // TODO hardcoded for now
Handler: handler,
client: client,
Version: irma.NewVersion(2, 0), // TODO hardcoded for now
Version: minVersion,
ServerName: "Email request",
request: sigrequest,
}
......@@ -163,13 +169,8 @@ func (client *Client) newQrSession(qr *irma.Qr, handler Handler) SessionDismisse
return nil
}
version, err := calcVersion(qr)
if err != nil {
session.fail(&irma.SessionError{ErrorType: irma.ErrorProtocolVersionNotSupported, Err: err})
return nil
}
session.Version = version
session.transport.SetHeader("X-IRMA-ProtocolVersion", version.String())
session.transport.SetHeader(minVersionHeader, minVersion.String())
session.transport.SetHeader(maxVersionHeader, maxVersion.String())
if !strings.HasSuffix(session.ServerURL, "/") {
session.ServerURL += "/"
}
......@@ -446,25 +447,6 @@ func (session *session) getProof() (interface{}, error) {
// Helper functions
func calcVersion(qr *irma.Qr) (*irma.ProtocolVersion, error) {
// Iterate supportedVersions in reverse sorted order (i.e. biggest major number first)
keys := make([]int, 0, len(supportedVersions))
for k := range supportedVersions {
keys = append(keys, k)
}
sort.Sort(sort.Reverse(sort.IntSlice(keys)))
for _, major := range keys {
for _, minor := range supportedVersions[major] {
aboveMinimum := major > qr.ProtocolVersion.Major || (major == qr.ProtocolVersion.Major && minor >= qr.ProtocolVersion.Minor)
underMaximum := major < qr.ProtocolMaxVersion.Major || (major == qr.ProtocolMaxVersion.Major && minor <= qr.ProtocolMaxVersion.Minor)
if aboveMinimum && underMaximum {
return irma.NewVersion(major, minor), nil
}
}
}
return nil, fmt.Errorf("No supported protocol version between %s and %s", qr.ProtocolVersion.String(), qr.ProtocolMaxVersion.String())
}
// checkKeyshareEnrollment checks if we are enrolled into all involved keyshare servers,
// and aborts the session if not
func (session *session) checkKeyshareEnrollment() bool {
......
......@@ -124,9 +124,7 @@ type Qr struct {
// Server with which to perform the session
URL string `json:"u"`
// Session type (disclosing, signing, issuing)
Type Action `json:"irmaqr"`
ProtocolVersion ProtocolVersion `json:"v"`
ProtocolMaxVersion ProtocolVersion `json:"vmax"`
Type Action `json:"irmaqr"`
}
type SchemeManagerRequest Qr
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment