Commit f1a0344e authored by Sietse Ringers's avatar Sietse Ringers
Browse files

feat: allow disclosure of null attributes

Null attributes are attributes with bigint value 0, meaning that the issuer skipped that attribute. Instead of aborting the session, the irmaclient now allows disclosures of null attributes, when the verifier requests it. This allows the irmaclient to prove that a particular attribute was not issued to it, and allows verifiers to request optional attributes alongside other attributes.
parent af76f3be
......@@ -13,9 +13,18 @@ import (
)
func requestorSessionHelper(t *testing.T, request irma.SessionRequest, client *irmaclient.Client) *server.SessionResult {
StartIrmaServer(t)
StartIrmaServer(t, false)
defer StopIrmaServer()
return requestorSesionWorker(t, request, client)
}
func requestorUpdatedSessionHelper(t *testing.T, request irma.SessionRequest, client *irmaclient.Client) *server.SessionResult {
StartIrmaServer(t, true)
defer StopIrmaServer()
return requestorSesionWorker(t, request, client)
}
func requestorSesionWorker(t *testing.T, request irma.SessionRequest, client *irmaclient.Client) *server.SessionResult {
if client == nil {
client, _ = parseStorage(t)
defer test.ClearTestStorage(t)
......@@ -46,7 +55,7 @@ func requestorSessionHelper(t *testing.T, request irma.SessionRequest, client *i
// Check that nonexistent IRMA identifiers in the session request fail the session
func TestRequestorInvalidRequest(t *testing.T) {
StartIrmaServer(t)
StartIrmaServer(t, false)
defer StopIrmaServer()
_, _, err := irmaServer.StartSession(irma.NewDisclosureRequest(
irma.NewAttributeTypeIdentifier("irma-demo.RU.foo.bar"),
......
......@@ -46,8 +46,12 @@ func StopRequestorServer() {
requestorServer.Stop()
}
func StartIrmaServer(t *testing.T) {
func StartIrmaServer(t *testing.T, updatedIrmaConf bool) {
testdata := test.FindTestdataFolder(t)
irmaconf := "irma_configuration"
if updatedIrmaConf {
irmaconf += "_updated"
}
logger := logrus.New()
logger.Level = logrus.ErrorLevel
......@@ -55,10 +59,9 @@ func StartIrmaServer(t *testing.T) {
var err error
irmaServer, err = irmaserver.New(&server.Configuration{
URL: "http://localhost:48680",
Logger: logger,
SchemesPath: filepath.Join(testdata, "irma_configuration"),
IssuerPrivateKeysPath: filepath.Join(testdata, "privatekeys"),
URL: "http://localhost:48680",
Logger: logger,
SchemesPath: filepath.Join(testdata, irmaconf),
})
require.NoError(t, err)
......
......@@ -46,9 +46,18 @@ func TestDefaultCredentialValidity(t *testing.T) {
sessionHelper(t, request, "issue", client)
}
func TestIssuanceOptionalEmptyAttributes(t *testing.T) {
func TestIssuanceDisclosureEmptyAttributes(t *testing.T) {
client, _ := parseStorage(t)
defer test.ClearTestStorage(t)
req := getNameIssuanceRequest()
sessionHelper(t, req, "issue", nil)
sessionHelper(t, req, "issue", client)
// Test disclosing our null attribute
req2 := getDisclosureRequest(irma.NewAttributeTypeIdentifier("irma-demo.MijnOverheid.fullName.prefix"))
res := requestorSessionHelper(t, req2, client)
require.Nil(t, res.Err)
require.Nil(t, res.Disclosed[0][0].RawValue)
}
func TestIssuanceOptionalZeroLengthAttributes(t *testing.T) {
......@@ -123,6 +132,21 @@ func TestAttributeByteEncoding(t *testing.T) {
sessionHelper(t, request, "issue", client)
}
func TestOutdatedClientIrmaConfiguration(t *testing.T) {
client, _ := parseStorage(t)
defer test.ClearTestStorage(t)
// Remove old studentCard credential from before support for optional attributes, and issue a new one
require.NoError(t, client.RemoveAllCredentials())
require.Nil(t, requestorSessionHelper(t, getIssuanceRequest(true), client).Err)
// client does not have updated irma_configuration with new attribute irma-demo.RU.studentCard.newAttribute,
// and the server does. Disclose an attribute from this credential. The client implicitly discloses value 0
// for the new attribute, and the server accepts.
req := getDisclosureRequest(irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.level"))
require.Nil(t, requestorUpdatedSessionHelper(t, req, client).Err)
}
func TestDisclosureNewAttributeUpdateSchemeManager(t *testing.T) {
client, _ := parseStorage(t)
defer test.ClearTestStorage(t)
......@@ -132,12 +156,32 @@ func TestDisclosureNewAttributeUpdateSchemeManager(t *testing.T) {
attrid := irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.newAttribute")
require.False(t, client.Configuration.CredentialTypes[credid].ContainsAttribute(attrid))
client.Configuration.SchemeManagers[schemeid].URL = "http://localhost:48681/irma_configuration_updated/irma-demo"
disclosureRequest := irma.NewDisclosureRequest(attrid)
// Remove old studentCard credential from before support for optional attributes, and issue a new one
require.NoError(t, client.RemoveAllCredentials())
require.Nil(t, requestorSessionHelper(t, getIssuanceRequest(true), client).Err)
_, err := client.Configuration.Download(disclosureRequest)
// Trigger downloading the updated irma_configuration using a disclosure request containing the
// new attribute, and inform the client
client.Configuration.SchemeManagers[schemeid].URL = "http://localhost:48681/irma_configuration_updated/irma-demo"
newAttrRequest := irma.NewDisclosureRequest(attrid)
downloaded, err := client.Configuration.Download(newAttrRequest)
require.NoError(t, err)
require.NoError(t, client.ConfigurationUpdated(downloaded))
// Our new attribute now exists in the configuration
require.True(t, client.Configuration.CredentialTypes[credid].ContainsAttribute(attrid))
// Disclose an old attribute (i.e. not newAttribute) to a server with an old configuration
// Since our client has a new configuration it hides the new attribute that is not yet in the
// server's configuration. All proofs are however valid as they should be and the server accepts.
levelRequest := getDisclosureRequest(irma.NewAttributeTypeIdentifier("irma-demo.RU.studentCard.level"))
require.Nil(t, requestorSessionHelper(t, levelRequest, client).Err)
// Disclose newAttribute to a server with a new configuration. This attribute was added
// after we received a credential without it, so its value in this credential is 0.
res := requestorUpdatedSessionHelper(t, newAttrRequest, client)
require.Nil(t, res.Err)
require.Nil(t, res.Disclosed[0][0].RawValue)
}
func TestIssueNewAttributeUpdateSchemeManager(t *testing.T) {
......
......@@ -173,8 +173,9 @@ type DisclosureChoice struct {
// An AttributeRequest asks for an instance of an attribute type, possibly requiring it to have
// a specified value, in a session request.
type AttributeRequest struct {
Type AttributeTypeIdentifier `json:"type"`
Value *string `json:"value"`
Type AttributeTypeIdentifier `json:"type"`
Value *string `json:"value"`
Required bool `json:"required"`
}
var (
......@@ -277,7 +278,9 @@ func (c *AttributeCon) UnmarshalJSON(bts []byte) error {
}
func (ar *AttributeRequest) Satisfy(attr AttributeTypeIdentifier, val *string) bool {
return ar.Type == attr && (ar.Value == nil || (val != nil && *ar.Value == *val))
return ar.Type == attr &&
(!ar.Required || val != nil) &&
(ar.Value == nil || (val != nil && *ar.Value == *val))
}
func (c AttributeCon) Satisfy(proofs gabi.ProofList, indices []*DisclosedAttributeIndex, conf *Configuration) (bool, []*DisclosedAttribute, error) {
......
......@@ -24,10 +24,9 @@ const (
ProofStatusMissingAttributes = ProofStatus("MISSING_ATTRIBUTES") // Proof does not contain all requested attributes
ProofStatusExpired = ProofStatus("EXPIRED") // Attributes were expired at proof creation time (now, or according to timestamp in case of abs)
AttributeProofStatusPresent = AttributeProofStatus("PRESENT") // Attribute is disclosed and matches the value
AttributeProofStatusExtra = AttributeProofStatus("EXTRA") // Attribute is disclosed, but wasn't requested in request
AttributeProofStatusMissing = AttributeProofStatus("MISSING") // Attribute is NOT disclosed, but should be according to request
AttributeProofStatusInvalidValue = AttributeProofStatus("INVALID_VALUE") // Attribute is disclosed, but has invalid value according to request
AttributeProofStatusPresent = AttributeProofStatus("PRESENT") // Attribute is disclosed and matches the value
AttributeProofStatusExtra = AttributeProofStatus("EXTRA") // Attribute is disclosed, but wasn't requested in request
AttributeProofStatusNull = AttributeProofStatus("NULL") // Attribute is disclosed but is null
)
// DisclosedAttribute represents a disclosed attribute.
......@@ -234,11 +233,15 @@ func parseAttribute(index int, metadata *MetadataAttribute, attr *big.Int) (*Dis
attrid = credtype.AttributeTypes[index-2].GetAttributeTypeIdentifier()
attrval = decodeAttribute(attr, metadata.Version())
}
status := AttributeProofStatusPresent
if attrval == nil {
status = AttributeProofStatusNull
}
return &DisclosedAttribute{
Identifier: attrid,
RawValue: attrval,
Value: NewTranslatedString(attrval),
Status: AttributeProofStatusPresent,
Status: status,
}, attrval, nil
}
......
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