Commit 1328a65d authored by Sietse Ringers's avatar Sietse Ringers
Browse files

feat: support optional disjunctions by specifying an empty inner conjunction

parent 59166f5f
......@@ -95,16 +95,16 @@ func (session *session) handlePostCommitments(commitments *irma.IssueCommitmentM
session.markAlive()
request := session.request.(*irma.IssuanceRequest)
discloseCount := len(request.Disclose)
if len(commitments.Proofs) != len(request.Credentials)+discloseCount {
return nil, session.fail(server.ErrorAttributesMissing, "")
discloseCount := len(commitments.Proofs) - len(request.Credentials)
if discloseCount < 0 {
return nil, session.fail(server.ErrorMalformedInput, "Received insufficient proofs")
}
// Compute list of public keys against which to verify the received proofs
disclosureproofs := irma.ProofList(commitments.Proofs[:discloseCount])
pubkeys, err := disclosureproofs.ExtractPublicKeys(session.conf.IrmaConfiguration)
if err != nil {
return nil, session.fail(server.ErrorInvalidProofs, err.Error())
return nil, session.fail(server.ErrorMalformedInput, err.Error())
}
for _, cred := range request.Credentials {
iss := cred.CredentialTypeID.IssuerIdentifier()
......@@ -149,7 +149,10 @@ func (session *session) handlePostCommitments(commitments *irma.IssueCommitmentM
pk, _ := session.conf.IrmaConfiguration.PublicKey(id, cred.KeyCounter)
sk, _ := session.conf.PrivateKey(id)
issuer := gabi.NewIssuer(sk, pk, one)
proof := commitments.Proofs[i+discloseCount].(*gabi.ProofU)
proof, ok := commitments.Proofs[i+discloseCount].(*gabi.ProofU)
if !ok {
return nil, session.fail(server.ErrorMalformedInput, "Received invalid issuance commitment")
}
attributes, err := cred.AttributeList(session.conf.IrmaConfiguration, 0x03)
if err != nil {
return nil, session.fail(server.ErrorIssuanceFailed, err.Error())
......
......@@ -2,6 +2,7 @@ package sessiontest
import (
"encoding/json"
"reflect"
"testing"
"github.com/privacybydesign/irmago"
......@@ -163,3 +164,57 @@ func TestConDisCon(t *testing.T) {
requestorSessionHelper(t, dr, client)
}
func TestOptionalDisclosure(t *testing.T) {
client := parseStorage(t)
university := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.university")
studentid := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.studentID")
radboud := "Radboud"
attrs1 := irma.AttributeConDisCon{
irma.AttributeDisCon{ // Including one non-optional disjunction is required in disclosure and signature sessions
irma.AttributeCon{irma.AttributeRequest{Type: university}},
},
irma.AttributeDisCon{
irma.AttributeCon{},
irma.AttributeCon{irma.AttributeRequest{Type: studentid}},
},
}
disclosed1 := [][]*irma.DisclosedAttribute{
{
{
RawValue: &radboud,
Value: map[string]string{"": radboud, "en": radboud, "nl": radboud},
Identifier: irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.university"),
Status: irma.AttributeProofStatusPresent,
},
},
{},
}
attrs2 := irma.AttributeConDisCon{ // In issuance sessions, it is allowed that all disjunctions are optional
irma.AttributeDisCon{
irma.AttributeCon{},
irma.AttributeCon{irma.AttributeRequest{Type: studentid}},
},
}
disclosed2 := [][]*irma.DisclosedAttribute{{}}
tests := []struct {
request irma.SessionRequest
attrs irma.AttributeConDisCon
disclosed [][]*irma.DisclosedAttribute
}{
{irma.NewDisclosureRequest(), attrs1, disclosed1},
{irma.NewSignatureRequest("message"), attrs1, disclosed1},
{getIssuanceRequest(true), attrs1, disclosed1},
{getIssuanceRequest(true), attrs2, disclosed2},
}
for _, args := range tests {
args.request.Disclosure().Disclose = args.attrs
// TestHandler always prefers the first option when given any choice, so it will not disclose the optional attribute
result := requestorSessionHelper(t, args.request, client)
require.True(t, reflect.DeepEqual(args.disclosed, result.Disclosed))
}
}
......@@ -12,6 +12,7 @@ import (
"github.com/privacybydesign/irmago/server/requestorserver"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
"github.com/x-cray/logrus-prefixed-formatter"
)
var (
......@@ -25,7 +26,7 @@ var (
func init() {
logger.Level = logrus.ErrorLevel
logger.Formatter = &logrus.TextFormatter{}
logger.Formatter = &prefixed.TextFormatter{ForceFormatting: true, ForceColors: true}
}
func StartRequestorServer(configuration *requestorserver.Configuration) {
......
......@@ -515,6 +515,13 @@ func (client *Client) Candidates(discon irma.AttributeDisCon) (
candidates = [][]*irma.AttributeIdentifier{}
for _, con := range discon {
if len(con) == 0 {
// An empty conjunction means the containing disjunction is optional
// so it is satisfied by sending no attributes
candidates = append(candidates, []*irma.AttributeIdentifier{})
continue
}
// Build a list containing, for each attribute in this conjunction, a list of credential
// instances containing the attribute. Writing schematically a sample conjunction of three
// attribute types as [ a.a.a.a, a.a.a.b, a.a.b.x ], we map this to:
......
......@@ -206,7 +206,7 @@ func (c AttributeCon) CredentialTypes() []CredentialTypeIdentifier {
func (c *AttributeCon) MarshalJSON() ([]byte, error) {
var vals bool
m := map[AttributeTypeIdentifier]*string{}
var l []AttributeTypeIdentifier
l := make([]AttributeTypeIdentifier, 0, len(*c))
for _, attr := range *c {
m[attr.Type] = attr.Value
......@@ -256,34 +256,37 @@ func (ar *AttributeRequest) Satisfy(attr AttributeTypeIdentifier, val *string) b
return ar.Type == attr && (ar.Value == nil || (val != nil && *ar.Value == *val))
}
func (c AttributeCon) Satisfy(proofs gabi.ProofList, indices []*DisclosedAttributeIndex, conf *Configuration) ([]*DisclosedAttribute, error) {
func (c AttributeCon) Satisfy(proofs gabi.ProofList, indices []*DisclosedAttributeIndex, conf *Configuration) (bool, []*DisclosedAttribute, error) {
if len(indices) < len(c) {
return nil, nil
return false, nil, nil
}
attrs := make([]*DisclosedAttribute, 0, len(c))
if len(c) == 0 {
return true, attrs, nil
}
for j := range c {
index := indices[j]
attr, val, err := extractAttribute(proofs, index, conf)
if err != nil {
return nil, err
return false, nil, err
}
if !c[j].Satisfy(attr.Identifier, val) {
return nil, nil
return false, nil, nil
}
attrs = append(attrs, attr)
}
return attrs, nil
return true, attrs, nil
}
func (dc AttributeDisCon) Satisfy(proofs gabi.ProofList, indices []*DisclosedAttributeIndex, conf *Configuration) ([]*DisclosedAttribute, error) {
func (dc AttributeDisCon) Satisfy(proofs gabi.ProofList, indices []*DisclosedAttributeIndex, conf *Configuration) (bool, []*DisclosedAttribute, error) {
for _, con := range dc {
attrs, err := con.Satisfy(proofs, indices, conf)
if len(attrs) > 0 || err != nil {
return attrs, err
satisfied, attrs, err := con.Satisfy(proofs, indices, conf)
if satisfied || err != nil {
return true, attrs, err
}
}
return nil, nil
return false, nil, nil
}
func (cdc AttributeConDisCon) Satisfy(disclosure *Disclosure, conf *Configuration) (bool, [][]*DisclosedAttribute, error) {
......@@ -294,11 +297,11 @@ func (cdc AttributeConDisCon) Satisfy(disclosure *Disclosure, conf *Configuratio
complete := true
for i, discon := range cdc {
attrs, err := discon.Satisfy(disclosure.Proofs, disclosure.Indices[i], conf)
satisfied, attrs, err := discon.Satisfy(disclosure.Proofs, disclosure.Indices[i], conf)
if err != nil {
return false, nil, err
}
if len(attrs) > 0 {
if satisfied {
list[i] = attrs
} else {
complete = false
......
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