Commit 1ac28e84 authored by Koen van Ingen's avatar Koen van Ingen
Browse files

Return proofresult for each separate attribute

parent 7914f1c0
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"math/big" "math/big"
"time" "time"
"fmt"
"github.com/go-errors/errors" "github.com/go-errors/errors"
"github.com/mhe/gabi" "github.com/mhe/gabi"
) )
...@@ -18,6 +19,25 @@ const ( ...@@ -18,6 +19,25 @@ const (
metadataLength = 1 + 3 + 2 + 2 + 16 metadataLength = 1 + 3 + 2 + 2 + 16
) )
type AttributeResultList struct {
AttributeResults []*AttributeResult
}
type AttributeResult struct {
AttributeValue TranslatedString // Value of the disclosed attribute
AttributeId AttributeTypeIdentifier
AttributeProofStatus AttributeProofStatus
}
type AttributeProofStatus string
const (
PRESENT = AttributeProofStatus("PRESENT") // Attribute is disclosed and matches the value
UNKNOWN = AttributeProofStatus("UNKNOWN") // Attribute is disclosed, but status is yet unknown
MISSING = AttributeProofStatus("MISSING") // Attribute is NOT disclosed, but should be according to request
INVALID_VALUE = AttributeProofStatus("INVALID_VALUE") // Attribute is disclosed, but has invalid value according to request
)
var ( var (
metadataVersion = []byte{0x02} metadataVersion = []byte{0x02}
...@@ -329,7 +349,8 @@ func (disjunction *AttributeDisjunction) Satisfied() bool { ...@@ -329,7 +349,8 @@ func (disjunction *AttributeDisjunction) Satisfied() bool {
// Helper function to check if an attribute is satisfied against a list of disclosed attributes // Helper function to check if an attribute is satisfied against a list of disclosed attributes
// This is the case if: // This is the case if:
// attribute is contained in disclosed AND if a value is present: equal to that value // attribute is contained in disclosed AND if a value is present: equal to that value
func isAttributeSatisfied(attribute AttributeTypeIdentifier, value string, disclosed []*CredentialInfo, conf *Configuration) bool { // al can be nil if you don't want to include attribute status for proof
func isAttributeSatisfied(attribute AttributeTypeIdentifier, value string, disclosed []*CredentialInfo, conf *Configuration, al *AttributeResultList) bool {
for _, cred := range disclosed { for _, cred := range disclosed {
credentialType := cred.GetCredentialType(conf) credentialType := cred.GetCredentialType(conf)
index, err := credentialType.IndexOf(attribute) index, err := credentialType.IndexOf(attribute)
...@@ -340,12 +361,19 @@ func isAttributeSatisfied(attribute AttributeTypeIdentifier, value string, discl ...@@ -340,12 +361,19 @@ func isAttributeSatisfied(attribute AttributeTypeIdentifier, value string, discl
} }
disclosedAttributeValue := cred.Attributes[index] disclosedAttributeValue := cred.Attributes[index]
// If it contains this attribute, check if value match (it must be disclosed (i.e. not nil) and match the value) // If it contains this attribute, check if value matches (it must be disclosed (i.e. not nil) and match the value)
// Attribute is Statiisfied if: // Attribute is satisfied if:
// - Attribute is disclosed (i.e. not nil) // - Attribute is disclosed (i.e. not nil)
// - Value is empty OR value equal to disclosedValue // - Value is empty OR value equal to disclosedValue
if disclosedAttributeValue != nil && (value == "" || disclosedAttributeValue["en"] == value) { // TODO: fix translation/attr typing if disclosedAttributeValue != nil {
return true if value == "" || disclosedAttributeValue["en"] == value { // TODO: fix translation/attr typing
al.SetProofStatus(attribute, disclosedAttributeValue, PRESENT)
return true
} else {
// If attribute is disclosed and present, but not equal to required value, mark it as invalid_value
// We won't return true and continue searching in other disclosed attributes
al.SetProofStatus(attribute, disclosedAttributeValue, INVALID_VALUE)
}
} }
} }
return false return false
...@@ -353,15 +381,26 @@ func isAttributeSatisfied(attribute AttributeTypeIdentifier, value string, discl ...@@ -353,15 +381,26 @@ func isAttributeSatisfied(attribute AttributeTypeIdentifier, value string, discl
// Check whether specified attributedisjunction satisfy a list of disclosed attributes // Check whether specified attributedisjunction satisfy a list of disclosed attributes
// We return true if one of the attributes in the disjunction is satisfied // We return true if one of the attributes in the disjunction is satisfied
func (disjunction *AttributeDisjunction) SatisfyDisclosed(disclosed []*CredentialInfo, conf *Configuration) bool { func (disjunction *AttributeDisjunction) SatisfyDisclosed(disclosed []*CredentialInfo, conf *Configuration, al *AttributeResultList) bool {
for _, attr := range disjunction.Attributes { for _, attr := range disjunction.Attributes {
value := disjunction.Values[attr] value := disjunction.Values[attr]
if isAttributeSatisfied(attr, value, disclosed, conf) { if isAttributeSatisfied(attr, value, disclosed, conf, al) {
return true return true
} }
} }
// Add all missing attributes
for _, attr := range disjunction.Attributes {
ar := AttributeResult{
AttributeId: attr,
AttributeProofStatus: MISSING,
}
if !al.ContainsAttributeId(ar.AttributeId) {
al.Append(ar)
}
}
return false return false
} }
...@@ -477,3 +516,87 @@ func (disjunction *AttributeDisjunction) UnmarshalJSON(bytes []byte) error { ...@@ -477,3 +516,87 @@ func (disjunction *AttributeDisjunction) UnmarshalJSON(bytes []byte) error {
return nil return nil
} }
// From here attributeResult related functions. TODO: move to separate file?
func (al *AttributeResultList) Append(result AttributeResult) {
al.AttributeResults = append(al.AttributeResults, &result)
}
func (al *AttributeResultList) ContainsAttributeId(attrId AttributeTypeIdentifier) bool {
for _, ar := range al.AttributeResults {
if ar.AttributeId == attrId {
return true
}
}
return false
}
func (al *AttributeResultList) String() string {
// TODO: pretty print?
str := "Attribute --- Value --- ProofStatus:"
for _, v := range al.AttributeResults {
str = str + "\n" + v.String()
}
return str
}
// Set the proof status to a new status for a specified attribute. An attribute is specified by an attributeTypeIdentifier
func (al *AttributeResultList) SetProofStatus(attrID AttributeTypeIdentifier, attrValue TranslatedString, status AttributeProofStatus) bool {
for _, ar := range al.AttributeResults {
// TODO: translation
if ar.AttributeId == attrID && ar.AttributeValue["en"] == attrValue["en"] {
ar.AttributeProofStatus = status
return true
}
}
return false
}
func (ar *AttributeResult) GetAttributeDescription(conf *Configuration) (*AttributeDescription, error) {
cred := conf.CredentialTypes[NewCredentialTypeIdentifier(ar.AttributeId.Parent())]
index, err := cred.IndexOf(ar.AttributeId)
if err != nil {
return nil, err
}
return &cred.Attributes[index], nil
}
func (ar *AttributeResult) String() string {
// TODO: translated string!
return fmt.Sprintf("%v --- %v --- %v",
ar.AttributeId,
ar.AttributeValue["en"],
ar.AttributeProofStatus)
}
func AttributeResultListFromDisclosed(disclosed []*CredentialInfo, conf *Configuration) *AttributeResultList {
al := AttributeResultList{}
for _, cred := range disclosed {
credentialType := cred.GetCredentialType(conf)
for _, attr := range credentialType.Attributes {
attrId := NewAttributeTypeIdentifier(cred.CredentialTypeID.String() + "." + attr.ID)
index, err := credentialType.IndexOf(attrId)
if err != nil {
// Specified credential does not contain this attribute, move to next
break
}
disclosedAttributeValue := cred.Attributes[index]
if disclosedAttributeValue != nil {
al.Append(AttributeResult{
AttributeValue: disclosedAttributeValue,
AttributeId: attrId,
AttributeProofStatus: UNKNOWN,
})
}
}
}
return &al
}
...@@ -10,12 +10,17 @@ import ( ...@@ -10,12 +10,17 @@ import (
"testing" "testing"
) )
type Result struct {
proofStatus ProofStatus
attributes *irma.AttributeResultList
}
type ManualSessionHandler struct { type ManualSessionHandler struct {
permissionHandler PermissionHandler permissionHandler PermissionHandler
pinHandler PinHandler pinHandler PinHandler
t *testing.T t *testing.T
errorChannel chan *irma.SessionError errorChannel chan *irma.SessionError
resultChannel chan ProofStatus resultChannel chan Result
sigRequest *irma.SignatureRequest // Request used to create signature sigRequest *irma.SignatureRequest // Request used to create signature
sigVerifyRequest *irma.SignatureRequest // Request used to verify signature sigVerifyRequest *irma.SignatureRequest // Request used to verify signature
} }
...@@ -51,7 +56,7 @@ func corruptProofString(proof string) string { ...@@ -51,7 +56,7 @@ func corruptProofString(proof string) string {
// Create a ManualSessionHandler for unit tests // Create a ManualSessionHandler for unit tests
func createManualSessionHandler(request string, invalidRequest string, t *testing.T) ManualSessionHandler { func createManualSessionHandler(request string, invalidRequest string, t *testing.T) ManualSessionHandler {
errorChannel := make(chan *irma.SessionError) errorChannel := make(chan *irma.SessionError)
resultChannel := make(chan ProofStatus) resultChannel := make(chan Result)
sigRequestJSON := []byte(request) sigRequestJSON := []byte(request)
invalidSigRequestJSON := []byte(invalidRequest) invalidSigRequestJSON := []byte(invalidRequest)
...@@ -84,8 +89,13 @@ func TestManualSession(t *testing.T) { ...@@ -84,8 +89,13 @@ func TestManualSession(t *testing.T) {
} }
// No errors, obtain proof result from channel // No errors, obtain proof result from channel
if result := <-ms.resultChannel; result != VALID { result := <-ms.resultChannel
t.Logf("Invalid proof result: %v Expected: %v", result, VALID) if ps := result.proofStatus; ps != VALID {
t.Logf("Invalid proof result: %v Expected: %v", ps, VALID)
t.Fail()
}
if attrStatus := result.attributes.AttributeResults[0].AttributeProofStatus; attrStatus != irma.PRESENT {
t.Logf("Invalid attribute result value: %v Expected: %v", attrStatus, irma.PRESENT)
t.Fail() t.Fail()
} }
test.ClearTestStorage(t) test.ClearTestStorage(t)
...@@ -127,8 +137,8 @@ func TestManualSessionInvalidNonce(t *testing.T) { ...@@ -127,8 +137,8 @@ func TestManualSessionInvalidNonce(t *testing.T) {
} }
// No errors, obtain proof result from channel // No errors, obtain proof result from channel
if result := <-ms.resultChannel; result != INVALID_CRYPTO { if result := <-ms.resultChannel; result.proofStatus != INVALID_CRYPTO {
t.Logf("Invalid proof result: %v Expected: %v", result, INVALID_CRYPTO) t.Logf("Invalid proof result: %v Expected: %v", result.proofStatus, INVALID_CRYPTO)
t.Fail() t.Fail()
} }
test.ClearTestStorage(t) test.ClearTestStorage(t)
...@@ -152,8 +162,19 @@ func TestManualSessionInvalidRequest(t *testing.T) { ...@@ -152,8 +162,19 @@ func TestManualSessionInvalidRequest(t *testing.T) {
} }
// No errors, obtain proof result from channel // No errors, obtain proof result from channel
if result := <-ms.resultChannel; result != MISSING_ATTRIBUTES { result := <-ms.resultChannel
t.Logf("Invalid proof result: %v Expected: %v", result, MISSING_ATTRIBUTES) if ps := result.proofStatus; ps != MISSING_ATTRIBUTES {
t.Logf("Invalid proof result: %v Expected: %v", ps, MISSING_ATTRIBUTES)
t.Fail()
}
// First attribute result is UNKOWN, since it is disclosed, but not matching the sigrequest
if attrStatus := result.attributes.AttributeResults[0].AttributeProofStatus; attrStatus != irma.UNKNOWN {
t.Logf("Invalid attribute result value: %v Expected: %v", attrStatus, irma.UNKNOWN)
t.Fail()
}
// Second attribute result is MISSING, because it is in the request but not disclosed
if attrStatus := result.attributes.AttributeResults[1].AttributeProofStatus; attrStatus != irma.MISSING {
t.Logf("Invalid attribute result value: %v Expected: %v", attrStatus, irma.MISSING)
t.Fail() t.Fail()
} }
test.ClearTestStorage(t) test.ClearTestStorage(t)
...@@ -177,8 +198,13 @@ func TestManualSessionInvalidAttributeValue(t *testing.T) { ...@@ -177,8 +198,13 @@ func TestManualSessionInvalidAttributeValue(t *testing.T) {
} }
// No errors, obtain proof result from channel // No errors, obtain proof result from channel
if result := <-ms.resultChannel; result != MISSING_ATTRIBUTES { result := <-ms.resultChannel
t.Logf("Invalid proof result: %v Expected: %v", result, MISSING_ATTRIBUTES) if ps := result.proofStatus; ps != MISSING_ATTRIBUTES {
t.Logf("Invalid proof result: %v Expected: %v", ps, MISSING_ATTRIBUTES)
t.Fail()
}
if attrStatus := result.attributes.AttributeResults[0].AttributeProofStatus; attrStatus != irma.INVALID_VALUE {
t.Logf("Invalid attribute result value: %v Expected: %v", attrStatus, irma.INVALID_VALUE)
t.Fail() t.Fail()
} }
test.ClearTestStorage(t) test.ClearTestStorage(t)
...@@ -200,8 +226,8 @@ func TestManualKeyShareSession(t *testing.T) { ...@@ -200,8 +226,8 @@ func TestManualKeyShareSession(t *testing.T) {
} }
// No errors, obtain proof result from channel // No errors, obtain proof result from channel
if result := <-ms.resultChannel; result != VALID { if result := <-ms.resultChannel; result.proofStatus != VALID {
t.Logf("Invalid proof result: %v Expected: %v", result, VALID) t.Logf("Invalid proof result: %v Expected: %v", result.proofStatus, VALID)
t.Fail() t.Fail()
} }
test.ClearTestStorage(t) test.ClearTestStorage(t)
...@@ -232,8 +258,8 @@ func TestManualSessionMultiProof(t *testing.T) { ...@@ -232,8 +258,8 @@ func TestManualSessionMultiProof(t *testing.T) {
} }
// No errors, obtain proof result from channel // No errors, obtain proof result from channel
if result := <-ms.resultChannel; result != VALID { if result := <-ms.resultChannel; result.proofStatus != VALID {
t.Logf("Invalid proof result: %v Expected: %v", result, VALID) t.Logf("Invalid proof result: %v Expected: %v", result.proofStatus, VALID)
t.Fail() t.Fail()
} }
test.ClearTestStorage(t) test.ClearTestStorage(t)
...@@ -254,8 +280,8 @@ func TestManualSessionInvalidProof(t *testing.T) { ...@@ -254,8 +280,8 @@ func TestManualSessionInvalidProof(t *testing.T) {
} }
// No errors, obtain proof result from channel // No errors, obtain proof result from channel
if result := <-ms.resultChannel; result != INVALID_CRYPTO { if result := <-ms.resultChannel; result.proofStatus != INVALID_CRYPTO {
t.Logf("Invalid proof result: %v Expected: %v", result, INVALID_CRYPTO) t.Logf("Invalid proof result: %v Expected: %v", result.proofStatus, INVALID_CRYPTO)
t.Fail() t.Fail()
} }
test.ClearTestStorage(t) test.ClearTestStorage(t)
...@@ -268,7 +294,8 @@ func (sh *ManualSessionHandler) Success(irmaAction irma.Action, result string) { ...@@ -268,7 +294,8 @@ func (sh *ManualSessionHandler) Success(irmaAction irma.Action, result string) {
result = corruptProofString(result) result = corruptProofString(result)
go func() { go func() {
sh.resultChannel <- VerifySig(client.Configuration, result, sh.sigVerifyRequest) proofStatus, attributeResultList := VerifySig(client.Configuration, result, sh.sigVerifyRequest)
sh.resultChannel <- Result{proofStatus, attributeResultList}
}() }()
} }
sh.errorChannel <- nil sh.errorChannel <- nil
......
...@@ -8,6 +8,12 @@ import ( ...@@ -8,6 +8,12 @@ import (
"math/big" "math/big"
) )
// TODO: move to irma package?
type ProofResult struct {
proofStatus ProofStatus // The overall proofstatus, should be VALID in order to accept the proof
attributes []irma.AttributeResult
}
type ProofStatus string type ProofStatus string
const ( const (
...@@ -58,28 +64,29 @@ func extractDisclosedCredentials(configuration *irma.Configuration, proofList *g ...@@ -58,28 +64,29 @@ func extractDisclosedCredentials(configuration *irma.Configuration, proofList *g
return credentials, nil return credentials, nil
} }
func checkProofWithRequest(configuration *irma.Configuration, proofList *gabi.ProofList, sigRequest *irma.SignatureRequest) ProofStatus { func checkProofWithRequest(configuration *irma.Configuration, proofList *gabi.ProofList, sigRequest *irma.SignatureRequest) (ProofStatus, *irma.AttributeResultList) {
credentials, err := extractDisclosedCredentials(configuration, proofList) credentials, err := extractDisclosedCredentials(configuration, proofList)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return INVALID_CRYPTO return INVALID_CRYPTO, nil
} }
al := irma.AttributeResultListFromDisclosed(credentials, configuration)
for _, content := range sigRequest.Content { for _, content := range sigRequest.Content {
if !content.SatisfyDisclosed(credentials, configuration) { if !content.SatisfyDisclosed(credentials, configuration, al) {
return MISSING_ATTRIBUTES return MISSING_ATTRIBUTES, al
} }
} }
// Check if a credential is expired // Check if a credential is expired
for _, cred := range credentials { for _, cred := range credentials {
if cred.IsExpired() { if cred.IsExpired() {
return EXPIRED return EXPIRED, al
} }
} }
return VALID return VALID, al
} }
// Verify an IRMA proof cryptographically // Verify an IRMA proof cryptographically
...@@ -95,7 +102,7 @@ func verify(configuration *irma.Configuration, proofList *gabi.ProofList, contex ...@@ -95,7 +102,7 @@ func verify(configuration *irma.Configuration, proofList *gabi.ProofList, contex
} }
// Verify a signature proof and check if the attributes match the attributes in the original request // Verify a signature proof and check if the attributes match the attributes in the original request
func VerifySig(configuration *irma.Configuration, proofString string, sigRequest *irma.SignatureRequest) ProofStatus { func VerifySig(configuration *irma.Configuration, proofString string, sigRequest *irma.SignatureRequest) (ProofStatus, *irma.AttributeResultList) {
// First, unmarshal proof and check if all the attributes in the proofstring match the signature request // First, unmarshal proof and check if all the attributes in the proofstring match the signature request
var proofList gabi.ProofList var proofList gabi.ProofList
...@@ -104,12 +111,12 @@ func VerifySig(configuration *irma.Configuration, proofString string, sigRequest ...@@ -104,12 +111,12 @@ func VerifySig(configuration *irma.Configuration, proofString string, sigRequest
err := proofList.UnmarshalJSON(proofBytes) err := proofList.UnmarshalJSON(proofBytes)
if err != nil { if err != nil {
fmt.Printf("Error unmarshalling JSON: %v\n", err) fmt.Printf("Error unmarshalling JSON: %v\n", err)
return INVALID_JSON return INVALID_JSON, nil
} }
// Now, cryptographically verify the signature // Now, cryptographically verify the signature
if !verify(configuration, &proofList, sigRequest.GetContext(), sigRequest.GetNonce(), true) { if !verify(configuration, &proofList, sigRequest.GetContext(), sigRequest.GetNonce(), true) {
return INVALID_CRYPTO return INVALID_CRYPTO, nil
} }
// Finally, check whether attribute values in proof satisfy the original signature request // Finally, check whether attribute values in proof satisfy the original signature request
......
Supports Markdown
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