Commit 8bac42af authored by Sietse Ringers's avatar Sietse Ringers
Browse files

feat: add backwards compatibility with old format session requests

parent 47b28f82
...@@ -33,7 +33,7 @@ func (session *session) handleGetRequest(min, max *irma.ProtocolVersion) (irma.S ...@@ -33,7 +33,7 @@ func (session *session) handleGetRequest(min, max *irma.ProtocolVersion) (irma.S
return nil, session.fail(server.ErrorProtocolVersion, "") return nil, session.fail(server.ErrorProtocolVersion, "")
} }
session.conf.Logger.WithFields(logrus.Fields{"session": session.token, "version": session.version.String()}).Debugf("Protocol version negotiated") session.conf.Logger.WithFields(logrus.Fields{"session": session.token, "version": session.version.String()}).Debugf("Protocol version negotiated")
session.request.Base().Version = session.version session.request.Base().ProtocolVersion = session.version
session.setStatus(server.StatusConnected) session.setStatus(server.StatusConnected)
return session.request, nil return session.request, nil
......
...@@ -112,10 +112,10 @@ func (th TestHandler) RequestVerificationPermission(request *irma.DisclosureRequ ...@@ -112,10 +112,10 @@ func (th TestHandler) RequestVerificationPermission(request *irma.DisclosureRequ
callback(true, &choice) callback(true, &choice)
} }
func (th TestHandler) RequestIssuancePermission(request *irma.IssuanceRequest, candidates [][][]*irma.AttributeIdentifier, ServerName irma.TranslatedString, callback irmaclient.PermissionHandler) { func (th TestHandler) RequestIssuancePermission(request *irma.IssuanceRequest, candidates [][][]*irma.AttributeIdentifier, ServerName irma.TranslatedString, callback irmaclient.PermissionHandler) {
th.RequestVerificationPermission(request.DisclosureRequest, candidates, ServerName, callback) th.RequestVerificationPermission(&request.DisclosureRequest, candidates, ServerName, callback)
} }
func (th TestHandler) RequestSignaturePermission(request *irma.SignatureRequest, candidates [][][]*irma.AttributeIdentifier, ServerName irma.TranslatedString, callback irmaclient.PermissionHandler) { func (th TestHandler) RequestSignaturePermission(request *irma.SignatureRequest, candidates [][][]*irma.AttributeIdentifier, ServerName irma.TranslatedString, callback irmaclient.PermissionHandler) {
th.RequestVerificationPermission(request.DisclosureRequest, candidates, ServerName, callback) th.RequestVerificationPermission(&request.DisclosureRequest, candidates, ServerName, callback)
} }
func (th TestHandler) RequestSchemeManagerPermission(manager *irma.SchemeManager, callback func(proceed bool)) { func (th TestHandler) RequestSchemeManagerPermission(manager *irma.SchemeManager, callback func(proceed bool)) {
callback(true) callback(true)
...@@ -168,7 +168,7 @@ func (th *ManualTestHandler) Success(result string) { ...@@ -168,7 +168,7 @@ func (th *ManualTestHandler) Success(result string) {
th.c <- retval th.c <- retval
} }
func (th *ManualTestHandler) RequestSignaturePermission(request *irma.SignatureRequest, candidates [][][]*irma.AttributeIdentifier, requesterName irma.TranslatedString, ph irmaclient.PermissionHandler) { func (th *ManualTestHandler) RequestSignaturePermission(request *irma.SignatureRequest, candidates [][][]*irma.AttributeIdentifier, requesterName irma.TranslatedString, ph irmaclient.PermissionHandler) {
th.RequestVerificationPermission(request.DisclosureRequest, candidates, requesterName, ph) th.RequestVerificationPermission(&request.DisclosureRequest, 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, candidates [][][]*irma.AttributeIdentifier, issuerName irma.TranslatedString, ph irmaclient.PermissionHandler) {
ph(true, nil) ph(true, nil)
......
...@@ -764,7 +764,7 @@ func (client *Client) ConstructCredentials(msg []*gabi.IssueSignatureMessage, re ...@@ -764,7 +764,7 @@ func (client *Client) ConstructCredentials(msg []*gabi.IssueSignatureMessage, re
continue continue
} }
sig := msg[i-offset] sig := msg[i-offset]
attrs, err := request.Credentials[i-offset].AttributeList(client.Configuration, irma.GetMetadataVersion(request.Base().Version)) attrs, err := request.Credentials[i-offset].AttributeList(client.Configuration, irma.GetMetadataVersion(request.Base().ProtocolVersion))
if err != nil { if err != nil {
return err return err
} }
......
...@@ -235,12 +235,12 @@ func (session *session) processSessionInfo() { ...@@ -235,12 +235,12 @@ func (session *session) processSessionInfo() {
} }
baserequest := session.request.Base() baserequest := session.request.Base()
confirmedProtocolVersion := baserequest.Version confirmedProtocolVersion := baserequest.ProtocolVersion
if confirmedProtocolVersion != nil { if confirmedProtocolVersion != nil {
session.Version = confirmedProtocolVersion session.Version = confirmedProtocolVersion
} else { } else {
session.Version = irma.NewVersion(2, 0) session.Version = irma.NewVersion(2, 0)
baserequest.Version = session.Version baserequest.ProtocolVersion = session.Version
} }
session.ServerName = serverName(session.Hostname, session.request, session.client.Configuration) session.ServerName = serverName(session.Hostname, session.request, session.client.Configuration)
......
...@@ -3,6 +3,7 @@ package irma ...@@ -3,6 +3,7 @@ package irma
import ( import (
"encoding/json" "encoding/json"
"path/filepath" "path/filepath"
"reflect"
"testing" "testing"
"time" "time"
...@@ -248,3 +249,216 @@ func TestAttributeDecoding(t *testing.T) { ...@@ -248,3 +249,216 @@ func TestAttributeDecoding(t *testing.T) {
oldString := decodeAttribute(oldAttribute, 2) oldString := decodeAttribute(oldAttribute, 2)
require.Equal(t, *oldString, expected) require.Equal(t, *oldString, expected)
} }
func TestSessionRequests(t *testing.T) {
attrval := "hello"
sigMessage := "message to be signed"
base := &DisclosureRequest{
BaseRequest: BaseRequest{Type: ActionDisclosing, Version: 2},
Disclose: AttributeConDisCon{
AttributeDisCon{
AttributeCon{NewAttributeRequest("irma-demo.MijnOverheid.ageLimits.over18")},
AttributeCon{NewAttributeRequest("irma-demo.MijnOverheid.ageLimits.over21")},
},
AttributeDisCon{
AttributeCon{AttributeRequest{Type: NewAttributeTypeIdentifier("irma-demo.MijnOverheid.fullName.firstname"), Value: &attrval}},
},
},
Labels: map[int]TranslatedString{0: trivialTranslation("Age limit"), 1: trivialTranslation("First name")},
}
tests := []struct {
oldJson, currentJson string
old, current, expected SessionRequest
}{
{
expected: base,
old: &DisclosureRequest{},
oldJson: `{
"type": "disclosing",
"content": [{
"label": "Age limit",
"attributes": [
"irma-demo.MijnOverheid.ageLimits.over18",
"irma-demo.MijnOverheid.ageLimits.over21"
]
},
{
"label": "First name",
"attributes": {
"irma-demo.MijnOverheid.fullName.firstname": "hello"
}
}]
}`,
current: &DisclosureRequest{},
currentJson: `{
"type": "disclosing",
"v": 2,
"disclose": [
[
[
"irma-demo.MijnOverheid.ageLimits.over18"
],
[
"irma-demo.MijnOverheid.ageLimits.over21"
]
],
[
{
"irma-demo.MijnOverheid.fullName.firstname": "hello"
}
]
],
"labels": {
"0": {
"en": "Age limit",
"nl": "Age limit"
},
"1": {
"en": "First name",
"nl": "First name"
}
}
}`,
},
{
expected: &SignatureRequest{
DisclosureRequest{BaseRequest{Type: ActionSigning, Version: 2}, base.Disclose, base.Labels},
sigMessage,
},
old: &SignatureRequest{},
oldJson: `{
"type": "signing",
"message": "message to be signed",
"content": [{
"label": "Age limit",
"attributes": [
"irma-demo.MijnOverheid.ageLimits.over18",
"irma-demo.MijnOverheid.ageLimits.over21"
]
},
{
"label": "First name",
"attributes": {
"irma-demo.MijnOverheid.fullName.firstname": "hello"
}
}]
}`,
current: &SignatureRequest{},
currentJson: `{
"type": "signing",
"v": 2,
"disclose": [
[
[
"irma-demo.MijnOverheid.ageLimits.over18"
],
[
"irma-demo.MijnOverheid.ageLimits.over21"
]
],
[
{
"irma-demo.MijnOverheid.fullName.firstname": "hello"
}
]
],
"labels": {
"0": {
"en": "Age limit",
"nl": "Age limit"
},
"1": {
"en": "First name",
"nl": "First name"
}
},
"message": "message to be signed"
}`,
},
{
expected: &IssuanceRequest{
DisclosureRequest: DisclosureRequest{BaseRequest{Type: ActionIssuing, Version: 2}, base.Disclose, base.Labels},
Credentials: []*CredentialRequest{
{
CredentialTypeID: NewCredentialTypeIdentifier("irma-demo.MijnOverheid.root"),
Attributes: map[string]string{"BSN": "12345"},
},
},
},
old: &IssuanceRequest{},
oldJson: `{
"type": "issuing",
"credentials": [{
"credential": "irma-demo.MijnOverheid.root",
"attributes": { "BSN": "12345" }
}],
"disclose": [{
"label": "Age limit",
"attributes": [
"irma-demo.MijnOverheid.ageLimits.over18",
"irma-demo.MijnOverheid.ageLimits.over21"
]
},
{
"label": "First name",
"attributes": {
"irma-demo.MijnOverheid.fullName.firstname": "hello"
}
}]
}`,
current: &IssuanceRequest{},
currentJson: `{
"type": "issuing",
"v": 2,
"credentials": [
{
"credential": "irma-demo.MijnOverheid.root",
"attributes": {
"BSN": "12345"
}
}
],
"disclose": [
[
[
"irma-demo.MijnOverheid.ageLimits.over18"
],
[
"irma-demo.MijnOverheid.ageLimits.over21"
]
],
[
{
"irma-demo.MijnOverheid.fullName.firstname": "hello"
}
]
],
"labels": {
"0": {
"en": "Age limit",
"nl": "Age limit"
},
"1": {
"en": "First name",
"nl": "First name"
}
}
}`,
},
}
for _, tst := range tests {
require.NoError(t, json.Unmarshal([]byte(tst.oldJson), tst.old))
require.NoError(t, json.Unmarshal([]byte(tst.currentJson), tst.current))
require.True(t, reflect.DeepEqual(tst.old, tst.expected), "Legacy %s did not unmarshal to expected value", reflect.TypeOf(tst.old).String())
require.True(t, reflect.DeepEqual(tst.current, tst.expected), "%s did not unmarshal to expected value", reflect.TypeOf(tst.old).String())
}
}
func trivialTranslation(str string) TranslatedString {
return TranslatedString{"en": str, "nl": str}
}
package irma
import (
"encoding/json"
"github.com/go-errors/errors"
)
type legacyAttributeDisjunction []AttributeRequest
type attributeDisjunction struct {
Label string
Attributes legacyAttributeDisjunction
}
type legacyDisclosureRequest struct {
BaseRequest
Content []attributeDisjunction `json:"content"`
}
func convertDisjunctions(disjunctions []attributeDisjunction) (
condiscon AttributeConDisCon, labels map[int]TranslatedString,
) {
labels = make(map[int]TranslatedString)
condiscon = make(AttributeConDisCon, len(disjunctions))
for i, dis := range disjunctions {
condiscon[i] = AttributeDisCon{}
for _, attr := range dis.Attributes {
condiscon[i] = append(condiscon[i], AttributeCon{{Type: attr.Type, Value: attr.Value}})
}
labels[i] = TranslatedString{"en": dis.Label, "nl": dis.Label}
}
return
}
func parseVersion(bts []byte) (int, error) {
var v struct {
Version int `json:"v"`
}
if err := json.Unmarshal(bts, &v); err != nil {
return 0, err
}
return v.Version, nil
}
func checkType(typ, expected Action) error {
if typ != expected {
return errors.New("not a " + expected + " session request")
}
return nil
}
// Reuses AttributeCon.UnmarshalJSON()
func (l *legacyAttributeDisjunction) UnmarshalJSON(bts []byte) error {
var con AttributeCon
if err := json.Unmarshal(bts, &con); err != nil {
return err
}
*l = legacyAttributeDisjunction(con)
return nil
}
func (dr *DisclosureRequest) UnmarshalJSON(bts []byte) (err error) {
var version int
if version, err = parseVersion(bts); err != nil {
return err
}
if version >= 2 {
type newDisclosureRequest DisclosureRequest // Same type with default JSON unmarshaler
var req newDisclosureRequest
if err = json.Unmarshal(bts, &req); err != nil {
return err
}
*dr = DisclosureRequest(req)
return nil
}
var legacy legacyDisclosureRequest
if err = json.Unmarshal(bts, &legacy); err != nil {
return err
}
dr.BaseRequest = legacy.BaseRequest
dr.Version = 2
dr.Disclose, dr.Labels = convertDisjunctions(legacy.Content)
return checkType(legacy.Type, ActionDisclosing)
}
func (sr *SignatureRequest) UnmarshalJSON(bts []byte) (err error) {
var version int
if version, err = parseVersion(bts); err != nil {
return err
}
if version >= 2 {
var req struct { // Identical type with default JSON unmarshaler
BaseRequest
Disclose AttributeConDisCon `json:"disclose"`
Labels map[int]TranslatedString `json:"labels"`
Message string `json"string"`
}
if err = json.Unmarshal(bts, &req); err != nil {
return err
}
*sr = SignatureRequest{
DisclosureRequest{
req.BaseRequest,
req.Disclose,
req.Labels,
},
req.Message,
}
return nil
}
var legacy struct {
legacyDisclosureRequest
Message string `json:"message"`
}
if err = json.Unmarshal(bts, &legacy); err != nil {
return err
}
sr.BaseRequest = legacy.BaseRequest
sr.Version = 2
sr.Disclose, sr.Labels = convertDisjunctions(legacy.Content)
sr.Message = legacy.Message
return checkType(legacy.Type, ActionSigning)
}
func (ir *IssuanceRequest) UnmarshalJSON(bts []byte) (err error) {
var version int
if version, err = parseVersion(bts); err != nil {
return err
}
if version >= 2 {
var req struct { // Identical type with default JSON unmarshaler
BaseRequest
Disclose AttributeConDisCon `json:"disclose"`
Labels map[int]TranslatedString `json:"labels"`
Credentials []*CredentialRequest `json:"credentials"`
}
if err = json.Unmarshal(bts, &req); err != nil {
return err
}
*ir = IssuanceRequest{
DisclosureRequest: DisclosureRequest{req.BaseRequest, req.Disclose, req.Labels},
Credentials: req.Credentials,
}
return nil
}
var legacy struct {
BaseRequest
Credentials []*CredentialRequest `json:"credentials"`
Disclose []attributeDisjunction `json:"disclose"`
}
if err = json.Unmarshal(bts, &legacy); err != nil {
return err
}
ir.BaseRequest = legacy.BaseRequest
ir.Version = 2
ir.Credentials = legacy.Credentials
ir.Disclose, ir.Labels = convertDisjunctions(legacy.Disclose)
return checkType(legacy.Type, ActionIssuing)
}
...@@ -19,11 +19,13 @@ import ( ...@@ -19,11 +19,13 @@ import (
type BaseRequest struct { type BaseRequest struct {
// Denotes session type, must be "disclosing", "signing" or "issuing" // Denotes session type, must be "disclosing", "signing" or "issuing"
Type Action `json:"type"` Type Action `json:"type"`
// Message version. Current version is 2.
Version int `json:"v"`
// Chosen by the IRMA server during the session // Chosen by the IRMA server during the session
Context *big.Int `json:"context,omitempty"` Context *big.Int `json:"context,omitempty"`
Nonce *big.Int `json:"nonce,omitempty"` Nonce *big.Int `json:"nonce,omitempty"`
Version *ProtocolVersion `json:"protocolVersion,omitempty"` ProtocolVersion *ProtocolVersion `json:"protocolVersion,omitempty"`
// cache for Identifiers() method // cache for Identifiers() method
ids *IrmaIdentifierSet ids *IrmaIdentifierSet
...@@ -38,7 +40,8 @@ type AttributeDisCon []AttributeCon ...@@ -38,7 +40,8 @@ type AttributeDisCon []AttributeCon
// AttributeConDisCon is only satisfied if all of the containing AttributeDisCon are satisfied. // AttributeConDisCon is only satisfied if all of the containing AttributeDisCon are satisfied.
type AttributeConDisCon []AttributeDisCon type AttributeConDisCon []AttributeDisCon
// A DisclosureRequest is a request to disclose certain attributes. // A DisclosureRequest is a request to disclose certain attributes. Construct new instances using
// NewDisclosureRequest().
type DisclosureRequest struct { type DisclosureRequest struct {
BaseRequest BaseRequest
...@@ -46,16 +49,18 @@ type DisclosureRequest struct { ...@@ -46,16 +49,18 @@ type DisclosureRequest struct {
Labels map[int]TranslatedString `json:"labels"` Labels map[int]TranslatedString `json:"labels"`
} }
// A SignatureRequest is a a request to sign a message with certain attributes. // A SignatureRequest is a a request to sign a message with certain attributes. Construct new
// instances using NewSignatureRequest().
type SignatureRequest struct { type SignatureRequest struct {
*DisclosureRequest DisclosureRequest
Message string `json:"message"` Message string `json:"message"`
} }
// An IssuanceRequest is a request to issue certain credentials, // An IssuanceRequest is a request to issue certain credentials,
// optionally also asking for certain attributes to be simultaneously disclosed. // optionally also asking for certain attributes to be simultaneously disclosed. Construct new
// instances using NewIssuanceRequest().
type IssuanceRequest struct { type IssuanceRequest struct {
*DisclosureRequest DisclosureRequest
Credentials []*CredentialRequest `json:"credentials"` Credentials []*CredentialRequest `json:"credentials"`
// Derived data // Derived data
...@@ -221,6 +226,15 @@ func (c *AttributeCon) MarshalJSON() ([]byte, error) { ...@@ -221,6 +226,15 @@ func (c *AttributeCon) MarshalJSON() ([]byte, error) {
func (c *AttributeCon) UnmarshalJSON(bts []byte) error { func (c *AttributeCon) UnmarshalJSON(bts []byte) error {
var err error var err error
var l []AttributeTypeIdentifier
if err = json.Unmarshal(bts, &l); err == nil {
for _, id := range l {
*c = append(*c, AttributeRequest{Type: id})
}
return nil
}
m := map[AttributeTypeIdentifier]*string{} m := map[AttributeTypeIdentifier]*string{}
if err = json.Unmarshal(bts, &m); err == nil { if err = json.Unmarshal(bts, &m); err == nil {
for id, val := range m { for id, val := range m {
...@@ -229,14 +243,13 @@ func (c *AttributeCon) UnmarshalJSON(bts []byte) error { ...@@ -229,14 +243,13 @@ func (c *AttributeCon) UnmarshalJSON(bts []byte) error {
return nil return nil
} }
var l []AttributeTypeIdentifier var s string
if err = json.Unmarshal(bts, &l); err == nil { if err = json.Unmarshal(bts, &s); err == nil {
for _, id := range l { *c = append(*c, NewAttributeRequest(s))
*c = append(*c, AttributeRequest{Type: id}) return nil
}
} }
return err return errors.New("Failed to unmarshal attribute conjunction")
} }
func (ar *AttributeRequest) Satisfy(attr AttributeTypeIdentifier, val *string) bool { func (ar *AttributeRequest) Satisfy(attr AttributeTypeIdentifier, val *string) bool {
...@@ -317,7 +330,7 @@ func (dr *DisclosureRequest) AddSingle(attr AttributeTypeIdentifier, value *stri ...@@ -317,7 +330,7 @@ func (dr *DisclosureRequest) AddSingle(attr AttributeTypeIdentifier, value *stri
func NewDisclosureRequest(attrs ...AttributeTypeIdentifier) *DisclosureRequest { func NewDisclosureRequest(attrs ...AttributeTypeIdentifier) *DisclosureRequest {
request := &DisclosureRequest{ request := &DisclosureRequest{
BaseRequest: BaseRequest{Type: ActionDisclosing}, BaseRequest: BaseRequest{Type: ActionDisclosing, Version: 2},
Labels: map[int]TranslatedString{}, Labels: map[int]TranslatedString{},
} }
for _, attr := range attrs { for _, attr := range attrs {
...@@ -330,7 +343,7 @@ func NewSignatureRequest(message string, attrs ...AttributeTypeIdentifier) *Sign ...@@ -330,7 +343,7 @@ func NewSignatureRequest(message string, attrs ...AttributeTypeIdentifier) *Sign
dr := NewDisclosureRequest(attrs...) dr := NewDisclosureRequest(attrs...)
dr.Type = ActionSigning dr.Type = ActionSigning
return &SignatureRequest{ return &SignatureRequest{
DisclosureRequest: dr, DisclosureRequest: *dr,
Message: message, Message: message,
} }
} }
...@@ -339,7 +352,7 @@ func NewIssuanceRequest(creds []*CredentialRequest, attrs ...AttributeTypeIdenti ...@@ -339,7 +352,7 @@ func NewIssuanceRequest(creds []*CredentialRequest, attrs ...AttributeTypeIdenti
dr :