Commit 855ea196 authored by Sietse Ringers's avatar Sietse Ringers
Browse files

feat: Breaking change: support conjunctions of disjunctions of conjunctions...

feat: Breaking change: support conjunctions of disjunctions of conjunctions when requesting attributes in any of the three supported session types

Instead of requesting several attributes, one can now request several groups of attributes. Within each such group, attributes belonging to the same credential type always come from the same credential instance. This affects all three session types, as in each of them attributes can be disclosed.
parent f5c59478
......@@ -4,10 +4,8 @@ import (
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"encoding/json"
"time"
"github.com/go-errors/errors"
"github.com/privacybydesign/gabi"
"github.com/privacybydesign/gabi/big"
)
......@@ -328,173 +326,3 @@ func shortToByte(x int) []byte {
binary.BigEndian.PutUint16(bytes, uint16(x))
return bytes
}
// A DisclosureChoice contains the attributes chosen to be disclosed.
type DisclosureChoice struct {
Attributes []*AttributeIdentifier
}
// An AttributeDisjunction encapsulates a list of possible attributes, one
// of which should be disclosed.
type AttributeDisjunction struct {
Label string
Attributes []AttributeTypeIdentifier
Values map[AttributeTypeIdentifier]*string
selected *AttributeTypeIdentifier
value *string
index *int
}
// An AttributeDisjunctionList is a list of AttributeDisjunctions.
type AttributeDisjunctionList []*AttributeDisjunction
// HasValues indicates if the attributes of this disjunction have values
// that should be satisfied.
func (disjunction *AttributeDisjunction) HasValues() bool {
return disjunction.Values != nil && len(disjunction.Values) != 0
}
// attemptSatisfy tries to match the specified attribute type and value against the current disjunction,
// returning true if the disjunction contains the specified attribute type. Note that if the disjunction
// has required values, then it is only considered satisfied by the specified attribute type and value
// if the required value matches the specified value.
func (disjunction *AttributeDisjunction) attemptSatisfy(id AttributeTypeIdentifier, value *string) bool {
var found bool
for index, attr := range disjunction.Attributes {
if attr == id {
found = true
disjunction.selected = &id
disjunction.index = &index
disjunction.value = value
if !disjunction.HasValues() || disjunction.Values[id] == value {
return true
}
}
}
return found
}
// satisfied indicates if this disjunction has a valid attribute type and value selected,
// matching one of the attributes in the disjunction and possibly also the corresponding required value.
func (disjunction *AttributeDisjunction) satisfied() bool {
if disjunction.index == nil {
return false
}
attr := disjunction.Attributes[*disjunction.index]
return !disjunction.HasValues() || disjunction.value == disjunction.Values[attr]
}
// MatchesConfig returns true if all attributes contained in the disjunction are
// present in the specified configuration.
func (disjunction *AttributeDisjunction) MatchesConfig(conf *Configuration) bool {
for ai := range disjunction.Values {
creddescription, exists := conf.CredentialTypes[ai.CredentialTypeIdentifier()]
if !exists {
return false
}
if !creddescription.ContainsAttribute(ai) {
return false
}
}
return true
}
// satisfied indicates whether each contained attribute disjunction has a chosen attribute.
func (dl AttributeDisjunctionList) satisfied() bool {
for _, disjunction := range dl {
if !disjunction.satisfied() {
return false
}
}
return true
}
// Find searches for and returns the disjunction that contains the specified attribute identifier, or nil if not found.
func (dl AttributeDisjunctionList) Find(ai AttributeTypeIdentifier) *AttributeDisjunction {
for _, disjunction := range dl {
for _, attr := range disjunction.Attributes {
if attr == ai {
return disjunction
}
}
}
return nil
}
// MarshalJSON marshals the disjunction to JSON.
func (disjunction *AttributeDisjunction) MarshalJSON() ([]byte, error) {
if !disjunction.HasValues() {
temp := struct {
Label string `json:"label"`
Attributes []AttributeTypeIdentifier `json:"attributes"`
}{
Label: disjunction.Label,
Attributes: disjunction.Attributes,
}
return json.Marshal(temp)
}
temp := struct {
Label string `json:"label"`
Attributes map[AttributeTypeIdentifier]*string `json:"attributes"`
}{
Label: disjunction.Label,
Attributes: disjunction.Values,
}
return json.Marshal(temp)
}
// UnmarshalJSON unmarshals an attribute disjunction from JSON.
func (disjunction *AttributeDisjunction) UnmarshalJSON(bytes []byte) error {
if disjunction.Values == nil {
disjunction.Values = make(map[AttributeTypeIdentifier]*string)
}
if disjunction.Attributes == nil {
disjunction.Attributes = make([]AttributeTypeIdentifier, 0, 3)
}
// We don't know if the json element "attributes" is a list, or a map.
// So we unmarshal it into a temporary struct that has interface{} as the
// type of "attributes", so that we can check which of the two it is.
temp := struct {
Label string `json:"label"`
Attributes interface{} `json:"attributes"`
}{}
if err := json.Unmarshal(bytes, &temp); err != nil {
return err
}
disjunction.Label = temp.Label
switch temp.Attributes.(type) {
case map[string]interface{}:
temp := struct {
Label string `json:"label"`
Attributes map[string]*string `json:"attributes"`
}{}
if err := json.Unmarshal(bytes, &temp); err != nil {
return err
}
for str, value := range temp.Attributes {
id := NewAttributeTypeIdentifier(str)
disjunction.Attributes = append(disjunction.Attributes, id)
disjunction.Values[id] = value
}
case []interface{}:
temp := struct {
Label string `json:"label"`
Attributes []string `json:"attributes"`
}{}
if err := json.Unmarshal(bytes, &temp); err != nil {
return err
}
for _, str := range temp.Attributes {
disjunction.Attributes = append(disjunction.Attributes, NewAttributeTypeIdentifier(str))
}
default:
return errors.New("could not parse attribute disjunction: element 'attributes' was incorrect")
}
return nil
}
......@@ -172,6 +172,24 @@ func (id *AttributeTypeIdentifier) UnmarshalText(text []byte) error {
return nil
}
func (set *IrmaIdentifierSet) join(other *IrmaIdentifierSet) {
for scheme := range other.SchemeManagers {
set.SchemeManagers[scheme] = struct{}{}
}
for issuer := range other.Issuers {
set.Issuers[issuer] = struct{}{}
}
for ct := range other.CredentialTypes {
set.CredentialTypes[ct] = struct{}{}
}
for issuer := range other.PublicKeys {
if len(set.PublicKeys[issuer]) == 0 {
set.PublicKeys[issuer] = make([]int, 0, len(other.PublicKeys[issuer]))
}
set.PublicKeys[issuer] = append(set.PublicKeys[issuer], other.PublicKeys[issuer]...)
}
}
func (set *IrmaIdentifierSet) Distributed(conf *Configuration) bool {
for id := range set.SchemeManagers {
if conf.SchemeManagers[id].Distributed() {
......
......@@ -33,7 +33,7 @@ func (session *session) handleGetRequest(min, max *irma.ProtocolVersion) (irma.S
return nil, session.fail(server.ErrorProtocolVersion, "")
}
session.conf.Logger.WithFields(logrus.Fields{"session": session.token, "version": session.version.String()}).Debugf("Protocol version negotiated")
session.request.SetVersion(session.version)
session.request.Base().Version = session.version
session.setStatus(server.StatusConnected)
return session.request, nil
......@@ -127,7 +127,7 @@ func (session *session) handlePostCommitments(commitments *irma.IssueCommitmentM
// Verify all proofs and check disclosed attributes, if any, against request
session.result.Disclosed, session.result.ProofStatus, err = commitments.Disclosure().VerifyAgainstDisjunctions(
session.conf.IrmaConfiguration, request.Disclose, request.Context, request.Nonce, pubkeys, false)
session.conf.IrmaConfiguration, request.Disclose, request.GetContext(), request.GetNonce(), pubkeys, false)
if err != nil {
if err == irma.ErrorMissingPublicKey {
return nil, session.fail(server.ErrorUnknownPublicKey, "")
......
......@@ -153,10 +153,13 @@ func purgeRequest(request irma.RequestorRequest) irma.RequestorRequest {
_ = json.Unmarshal(bts, cpy)
// Remove required attribute values from any attributes to be disclosed
attrs := cpy.(irma.RequestorRequest).SessionRequest().ToDisclose()
for _, disjunction := range attrs {
disjunction.Values = nil
}
_ = cpy.(irma.RequestorRequest).SessionRequest().Disclosure().Disclose.Iterate(
func(attr *irma.AttributeRequest) error {
attr.Value = nil
return nil
},
)
// Remove attribute values from attributes to be issued
if isreq, ok := cpy.(*irma.IdentityProviderRequest); ok {
for _, cred := range isreq.Request.Credentials {
......
......@@ -162,8 +162,8 @@ func (s *Server) newSession(action irma.Action, request irma.RequestorRequest) *
s.conf.Logger.WithFields(logrus.Fields{"session": ses.token}).Debug("New session started")
nonce, _ := gabi.RandomBigInt(gabi.DefaultSystemParameters[2048].Lstatzk)
ses.request.SetNonce(nonce)
ses.request.SetContext(one)
ses.request.Base().Nonce = nonce
ses.request.Base().Context = one
s.sessions.add(ses)
return ses
......
......@@ -2,7 +2,9 @@ package sessiontest
import (
"encoding/json"
"math/rand"
"testing"
"time"
"github.com/pkg/errors"
"github.com/privacybydesign/irmago"
......@@ -94,36 +96,32 @@ func (th TestHandler) Failure(err *irma.SessionError) {
th.t.Fatal(err)
}
}
func (th TestHandler) UnsatisfiableRequest(serverName irma.TranslatedString, missing irma.AttributeDisjunctionList) {
func (th TestHandler) UnsatisfiableRequest(serverName irma.TranslatedString, missing map[int]map[int]irma.AttributeCon) {
th.Failure(&irma.SessionError{
ErrorType: irma.ErrorType("UnsatisfiableRequest"),
})
}
func (th TestHandler) RequestVerificationPermission(request irma.DisclosureRequest, ServerName irma.TranslatedString, callback irmaclient.PermissionHandler) {
func (th TestHandler) RequestVerificationPermission(request *irma.DisclosureRequest, ServerName irma.TranslatedString, callback irmaclient.PermissionHandler) {
choice := &irma.DisclosureChoice{
Attributes: []*irma.AttributeIdentifier{},
Attributes: [][]*irma.AttributeIdentifier{},
}
var candidates []*irma.AttributeIdentifier
for _, disjunction := range request.Content {
candidates = th.client.Candidates(disjunction)
var candidates [][]*irma.AttributeIdentifier
for _, disjunction := range request.Disclose {
candidates, _ = th.client.Candidates(disjunction)
if len(candidates) == 0 {
th.Failure(&irma.SessionError{Err: errors.New("No disclosure candidates found")})
}
choice.Attributes = append(choice.Attributes, candidates[0])
choice.Attributes = append(choice.Attributes, candidates[rand.Intn(len(candidates))])
}
if len(th.expectedServerName) != 0 {
require.Equal(th.t, th.expectedServerName, ServerName)
}
callback(true, choice)
}
func (th TestHandler) RequestIssuancePermission(request irma.IssuanceRequest, ServerName irma.TranslatedString, callback irmaclient.PermissionHandler) {
dreq := irma.DisclosureRequest{
BaseRequest: request.BaseRequest,
Content: request.Disclose,
}
th.RequestVerificationPermission(dreq, ServerName, callback)
func (th TestHandler) RequestIssuancePermission(request *irma.IssuanceRequest, ServerName irma.TranslatedString, callback irmaclient.PermissionHandler) {
th.RequestVerificationPermission(request.DisclosureRequest, ServerName, callback)
}
func (th TestHandler) RequestSignaturePermission(request irma.SignatureRequest, ServerName irma.TranslatedString, callback irmaclient.PermissionHandler) {
func (th TestHandler) RequestSignaturePermission(request *irma.SignatureRequest, ServerName irma.TranslatedString, callback irmaclient.PermissionHandler) {
th.RequestVerificationPermission(request.DisclosureRequest, ServerName, callback)
}
func (th TestHandler) RequestSchemeManagerPermission(manager *irma.SchemeManager, callback func(proceed bool)) {
......@@ -176,10 +174,10 @@ func (th *ManualTestHandler) Success(result string) {
th.c <- retval
}
func (th *ManualTestHandler) RequestSignaturePermission(request irma.SignatureRequest, requesterName irma.TranslatedString, ph irmaclient.PermissionHandler) {
func (th *ManualTestHandler) RequestSignaturePermission(request *irma.SignatureRequest, requesterName irma.TranslatedString, ph irmaclient.PermissionHandler) {
th.RequestVerificationPermission(request.DisclosureRequest, requesterName, ph)
}
func (th *ManualTestHandler) RequestIssuancePermission(request irma.IssuanceRequest, issuerName irma.TranslatedString, ph irmaclient.PermissionHandler) {
func (th *ManualTestHandler) RequestIssuancePermission(request *irma.IssuanceRequest, issuerName irma.TranslatedString, ph irmaclient.PermissionHandler) {
ph(true, nil)
}
......@@ -187,10 +185,14 @@ func (th *ManualTestHandler) RequestIssuancePermission(request irma.IssuanceRequ
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, verifierName irma.TranslatedString, ph irmaclient.PermissionHandler) {
func (th *ManualTestHandler) RequestVerificationPermission(request *irma.DisclosureRequest, verifierName irma.TranslatedString, ph irmaclient.PermissionHandler) {
var choice irma.DisclosureChoice
for _, cand := range request.Candidates {
choice.Attributes = append(choice.Attributes, cand[0])
}
ph(true, &choice)
}
func init() {
rand.Seed(time.Now().UnixNano())
}
......@@ -11,12 +11,12 @@ import (
)
func TestManualKeyShareSession(t *testing.T) {
request := "{\"nonce\": 0, \"context\": 0, \"type\": \"signing\", \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"test.test.mijnirma.email\"]}]}"
request := irma.NewSignatureRequest("I owe you everything", irma.NewAttributeTypeIdentifier("test.test.mijnirma.email"))
ms := createManualSessionHandler(t, nil)
_, status := manualSessionHelper(t, nil, ms, request, request, false)
require.Equal(t, irma.ProofStatusValid, status)
_, status = manualSessionHelper(t, nil, ms, request, "", false)
_, status = manualSessionHelper(t, nil, ms, request, nil, false)
require.Equal(t, irma.ProofStatusValid, status)
}
......@@ -44,20 +44,10 @@ func TestKeyshareSessions(t *testing.T) {
sessionHelper(t, issuanceRequest, "issue", client)
disclosureRequest := getDisclosureRequest(id)
disclosureRequest.Content = append(disclosureRequest.Content,
&irma.AttributeDisjunction{
Label: "foo",
Attributes: []irma.AttributeTypeIdentifier{irma.NewAttributeTypeIdentifier("test.test.mijnirma.email")},
},
)
disclosureRequest.AddSingle(irma.NewAttributeTypeIdentifier("test.test.mijnirma.email"), nil, nil)
sessionHelper(t, disclosureRequest, "verification", client)
sigRequest := getSigningRequest(id)
sigRequest.Content = append(sigRequest.Content,
&irma.AttributeDisjunction{
Label: "foo",
Attributes: []irma.AttributeTypeIdentifier{irma.NewAttributeTypeIdentifier("test.test.mijnirma.email")},
},
)
sigRequest.AddSingle(irma.NewAttributeTypeIdentifier("test.test.mijnirma.email"), nil, nil)
sessionHelper(t, sigRequest, "signature", client)
}
......@@ -65,8 +65,7 @@ func TestLogging(t *testing.T) {
require.NoError(t, err)
require.Equal(t, irma.ProofStatusValid, status)
require.NotEmpty(t, attrs)
require.Equal(t, attrid, attrs[0].Identifier)
require.Equal(t, "s1234567", attrs[0].Value["en"])
require.Equal(t, attrid, attrs[0][0].Identifier)
test.ClearTestStorage(t)
}
......@@ -49,39 +49,20 @@ func parseStorage(t *testing.T) *irmaclient.Client {
}
func getDisclosureRequest(id irma.AttributeTypeIdentifier) *irma.DisclosureRequest {
return &irma.DisclosureRequest{
BaseRequest: irma.BaseRequest{Type: irma.ActionDisclosing},
Content: irma.AttributeDisjunctionList([]*irma.AttributeDisjunction{{
Label: "foo",
Attributes: []irma.AttributeTypeIdentifier{id},
}}),
}
return irma.NewDisclosureRequest(id)
}
func getSigningRequest(id irma.AttributeTypeIdentifier) *irma.SignatureRequest {
return &irma.SignatureRequest{
Message: "test",
DisclosureRequest: irma.DisclosureRequest{
BaseRequest: irma.BaseRequest{Type: irma.ActionSigning},
Content: irma.AttributeDisjunctionList([]*irma.AttributeDisjunction{{
Label: "foo",
Attributes: []irma.AttributeTypeIdentifier{id},
}}),
},
}
return irma.NewSignatureRequest("test", id)
}
func getIssuanceRequest(defaultValidity bool) *irma.IssuanceRequest {
temp := irma.Timestamp(irma.FloorToEpochBoundary(time.Now().AddDate(1, 0, 0)))
var expiry *irma.Timestamp
if !defaultValidity {
expiry = &temp
}
return &irma.IssuanceRequest{
BaseRequest: irma.BaseRequest{Type: irma.ActionIssuing},
Credentials: []*irma.CredentialRequest{
return irma.NewIssuanceRequest([]*irma.CredentialRequest{
{
Validity: expiry,
CredentialTypeID: irma.NewCredentialTypeIdentifier("irma-demo.RU.studentCard"),
......@@ -92,16 +73,12 @@ func getIssuanceRequest(defaultValidity bool) *irma.IssuanceRequest {
"level": "42",
},
},
},
}
})
}
func getNameIssuanceRequest() *irma.IssuanceRequest {
expiry := irma.Timestamp(irma.NewMetadataAttribute(0).Expiry())
req := &irma.IssuanceRequest{
BaseRequest: irma.BaseRequest{Type: irma.ActionIssuing},
Credentials: []*irma.CredentialRequest{
return irma.NewIssuanceRequest([]*irma.CredentialRequest{
{
Validity: &expiry,
CredentialTypeID: irma.NewCredentialTypeIdentifier("irma-demo.MijnOverheid.fullName"),
......@@ -111,10 +88,8 @@ func getNameIssuanceRequest() *irma.IssuanceRequest {
"familyname": "Stuivezand",
},
},
},
}
})
return req
}
func getSpecialIssuanceRequest(defaultValidity bool, attribute string) *irma.IssuanceRequest {
......@@ -125,9 +100,7 @@ func getSpecialIssuanceRequest(defaultValidity bool, attribute string) *irma.Iss
func getCombinedIssuanceRequest(id irma.AttributeTypeIdentifier) *irma.IssuanceRequest {
request := getIssuanceRequest(false)
request.Disclose = irma.AttributeDisjunctionList{
&irma.AttributeDisjunction{Label: "foo", Attributes: []irma.AttributeTypeIdentifier{id}},
}
request.AddSingle(id, nil, nil)
return request
}
......
......@@ -23,13 +23,16 @@ func createManualSessionHandler(t *testing.T, client *irmaclient.Client) *Manual
}
}
func manualSessionHelper(t *testing.T, client *irmaclient.Client, h *ManualTestHandler, request string, verifyAs string, corrupt bool) ([]*irma.DisclosedAttribute, irma.ProofStatus) {
func manualSessionHelper(t *testing.T, client *irmaclient.Client, h *ManualTestHandler, request, verifyAs irma.SessionRequest, corrupt bool) ([][]*irma.DisclosedAttribute, irma.ProofStatus) {
if client == nil {
client = parseStorage(t)
defer test.ClearTestStorage(t)
}
client.NewSession(request, h)
bts, err := json.Marshal(request)
require.NoError(t, err)
client.NewSession(string(bts), h)
result := <-h.c
if result.Err != nil {
......@@ -38,26 +41,18 @@ func manualSessionHelper(t *testing.T, client *irmaclient.Client, h *ManualTestH
switch h.action {
case irma.ActionDisclosing:
verifyasRequest := &irma.DisclosureRequest{}
err := json.Unmarshal([]byte(verifyAs), verifyasRequest)
require.NoError(t, err)
list, status, err := result.DisclosureResult.Verify(client.Configuration, verifyasRequest)
r, _ := verifyAs.(*irma.DisclosureRequest)
list, status, err := result.DisclosureResult.Verify(client.Configuration, r)
require.NoError(t, err)
return list, status
case irma.ActionSigning:
var verifyasRequest *irma.SignatureRequest
if verifyAs != "" {
verifyasRequest = &irma.SignatureRequest{}
err := json.Unmarshal([]byte(verifyAs), verifyasRequest)
require.NoError(t, err)
}
if corrupt {
// Interesting: modifying C results in INVALID_CRYPTO; modifying A or an attribute results in INVALID_TIMESTAMP
i := result.SignatureResult.Signature[0].(*gabi.ProofD).C
i.Add(i, big.NewInt(16))
}
list, status, err := result.SignatureResult.Verify(client.Configuration, verifyasRequest)
r, _ := verifyAs.(*irma.SignatureRequest)
list, status, err := result.SignatureResult.Verify(client.Configuration, r)
require.NoError(t, err)
return list, status
default:
......@@ -66,21 +61,25 @@ func manualSessionHelper(t *testing.T, client *irmaclient.Client, h *ManualTestH
}
func TestManualSession(t *testing.T) {
request := "{\"nonce\": 42, \"context\": 1337, \"type\": \"signing\", \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
request := irma.NewSignatureRequest("I owe you everything", irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID"))
request.Nonce = big.NewInt(42)
ms := createManualSessionHandler(t, nil)
attrs, status := manualSessionHelper(t, nil, ms, request, request, false)
require.Equal(t, irma.ProofStatusValid, status)
require.Equal(t, irma.AttributeProofStatusPresent, attrs[0].Status)
attrs, status = manualSessionHelper(t, nil, ms, request, "", false)
require.Equal(t, irma.AttributeProofStatusPresent, attrs[0][0].Status)
attrs, status = manualSessionHelper(t, nil, ms, request, nil, false)
require.Equal(t, irma.ProofStatusValid, status)
require.Equal(t, irma.AttributeProofStatusExtra, attrs[0].Status)
require.Equal(t, irma.AttributeProofStatusExtra, attrs[0][0].Status)
}
// Test if proof verification fails with status 'ERROR_CRYPTO' if we verify it with an invalid nonce
func TestManualSessionInvalidNonce(t *testing.T) {
request := "{\"nonce\": 0, \"context\": 0, \"type\": \"signing\", \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
invalidRequest := "{\"nonce\": 1, \"context\": 0, \"type\": \"signing\", \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
request := irma.NewSignatureRequest("I owe you everything", irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID"))
invalidRequest := irma.NewSignatureRequest("I owe you everything", irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID"))
invalidRequest.Nonce = big.NewInt(1)
ms := createManualSessionHandler(t, nil)
_, status := manualSessionHelper(t, nil, ms, request, invalidRequest, false)
......@@ -89,27 +88,25 @@ func TestManualSessionInvalidNonce(t *testing.T) {
// Test if proof verification fails with status 'MISSING_ATTRIBUTES' if we provide it with a non-matching signature request
func TestManualSessionInvalidRequest(t *testing.T) {
request := "{\"nonce\": 0, \"context\": 0, \"type\": \"signing\", \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
invalidRequest := "{\"nonce\": 0, \"context\": 0, \"type\": \"signing\", \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.university\"]}]}"
request := irma.NewSignatureRequest("I owe you everything", irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID"))
invalidRequest := irma.NewSignatureRequest("I owe you everything", irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.university"))
ms := createManualSessionHandler(t, nil)
attrs, status := manualSessionHelper(t, nil, ms, request, invalidRequest, false)
_, status := manualSessionHelper(t, nil, ms, request, invalidRequest, false)
require.Equal(t, irma.ProofStatusMissingAttributes, status)
// First attribute result is MISSING, because it is in the request but not disclosed
require.Equal(t, irma.AttributeProofStatusMissing, attrs[0].Status)
// Second attribute result is EXTRA, since it is disclosed, but not matching the sigrequest
require.Equal(t, irma.AttributeProofStatusExtra, attrs[1].Status)
}
// Test if proof verification fails with status 'MISSING_ATTRIBUTES' if we provide it with invalid attribute values
func TestManualSessionInvalidAttributeValue(t *testing.T) {
request := "{\"nonce\": 0, \"context\": 0, \"type\": \"signing\", \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":{\"irma-demo.RU.studentCard.studentID\": \"456\"}}]}"
invalidRequest := "{\"nonce\": 0, \"context\": 0, \"type\": \"signing\", \