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

Rename/refactor some stuff in verification

parent 903aa88c
......@@ -172,11 +172,11 @@ func disabledTestLogging(t *testing.T) {
sig, err := entry.GetSignedMessage()
require.NoError(t, err)
require.NotNil(t, sig)
result := sig.Verify(client.Configuration, nil)
require.Equal(t, irma.ProofStatusValid, result.Status)
require.NotEmpty(t, result.Attributes)
require.Equal(t, result.Attributes[0].Identifier, attrid)
require.Equal(t, "s1234567", result.Attributes[0].Value["en"])
attrs, status := sig.Verify(client.Configuration, nil)
require.Equal(t, irma.ProofStatusValid, status)
require.NotEmpty(t, attrs)
require.Equal(t, attrs[0].Identifier, attrid)
require.Equal(t, "s1234567", attrs[0].Value["en"])
test.ClearTestStorage(t)
}
......
......@@ -22,7 +22,7 @@ func createManualSessionHandler(t *testing.T, client *Client) *ManualTestHandler
}
}
func manualSessionHelper(t *testing.T, client *Client, h *ManualTestHandler, request string, verifyAs string, corrupt bool) *irma.VerificationResult {
func manualSessionHelper(t *testing.T, client *Client, h *ManualTestHandler, request string, verifyAs string, corrupt bool) ([]*irma.DisclosedAttribute, irma.ProofStatus) {
init := client == nil
if init {
client = parseStorage(t)
......@@ -56,7 +56,7 @@ func manualSessionHelper(t *testing.T, client *Client, h *ManualTestHandler, req
}
return result.SignatureResult.Verify(client.Configuration, verifyasRequest)
default:
return nil
return nil, ""
}
}
......@@ -64,13 +64,12 @@ 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\"]}]}"
ms := createManualSessionHandler(t, nil)
result := manualSessionHelper(t, nil, ms, request, request, false)
require.Equal(t, irma.ProofStatusValid, result.Status)
require.Equal(t, irma.AttributeProofStatusPresent, result.Attributes[0].Status)
result = manualSessionHelper(t, nil, ms, request, "", false)
require.Equal(t, irma.ProofStatusValid, result.Status)
require.Equal(t, irma.AttributeProofStatusExtra, result.Attributes[0].Status)
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.ProofStatusValid, status)
require.Equal(t, irma.AttributeProofStatusExtra, attrs[0].Status)
test.ClearTestStorage(t)
}
......@@ -80,9 +79,9 @@ 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\"]}]}"
ms := createManualSessionHandler(t, nil)
result := manualSessionHelper(t, nil, ms, request, invalidRequest, false)
_, status := manualSessionHelper(t, nil, ms, request, invalidRequest, false)
require.Equal(t, irma.ProofStatusUnmatchedRequest, result.Status)
require.Equal(t, irma.ProofStatusUnmatchedRequest, status)
test.ClearTestStorage(t)
}
......@@ -92,13 +91,13 @@ 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\"]}]}"
ms := createManualSessionHandler(t, nil)
result := manualSessionHelper(t, nil, ms, request, invalidRequest, false)
attrs, status := manualSessionHelper(t, nil, ms, request, invalidRequest, false)
require.Equal(t, irma.ProofStatusMissingAttributes, result.Status)
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, result.Attributes[0].Status)
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, result.Attributes[1].Status)
require.Equal(t, irma.AttributeProofStatusExtra, attrs[1].Status)
test.ClearTestStorage(t)
}
......@@ -108,10 +107,10 @@ 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\", \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":{\"irma-demo.RU.studentCard.studentID\": \"123\"}}]}"
ms := createManualSessionHandler(t, nil)
result := manualSessionHelper(t, nil, ms, request, invalidRequest, false)
attrs, status := manualSessionHelper(t, nil, ms, request, invalidRequest, false)
require.Equal(t, irma.ProofStatusMissingAttributes, result.Status)
require.Equal(t, irma.AttributeProofStatusInvalidValue, result.Attributes[0].Status)
require.Equal(t, irma.ProofStatusMissingAttributes, status)
require.Equal(t, irma.AttributeProofStatusInvalidValue, attrs[0].Status)
test.ClearTestStorage(t)
}
......@@ -120,11 +119,10 @@ 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\"]}]}"
ms := createManualSessionHandler(t, nil)
result := manualSessionHelper(t, nil, ms, request, request, false)
require.Equal(t, irma.ProofStatusValid, result.Status)
result = manualSessionHelper(t, nil, ms, request, "", false)
require.Equal(t, irma.ProofStatusValid, result.Status)
_, status := manualSessionHelper(t, nil, ms, request, request, false)
require.Equal(t, irma.ProofStatusValid, status)
_, status = manualSessionHelper(t, nil, ms, request, "", false)
require.Equal(t, irma.ProofStatusValid, status)
test.ClearTestStorage(t)
}
......@@ -141,15 +139,14 @@ func TestManualSessionMultiProof(t *testing.T) {
ms := createManualSessionHandler(t, client)
result := manualSessionHelper(t, client, ms, request, request, false)
require.Equal(t, irma.ProofStatusValid, result.Status)
require.Equal(t, irma.AttributeProofStatusPresent, result.Attributes[0].Status)
require.Equal(t, irma.AttributeProofStatusPresent, result.Attributes[1].Status)
result = manualSessionHelper(t, client, ms, request, "", false)
require.Equal(t, irma.ProofStatusValid, result.Status)
require.Equal(t, irma.AttributeProofStatusExtra, result.Attributes[0].Status)
require.Equal(t, irma.AttributeProofStatusExtra, result.Attributes[1].Status)
attrs, status := manualSessionHelper(t, client, ms, request, request, false)
require.Equal(t, irma.ProofStatusValid, status)
require.Equal(t, irma.AttributeProofStatusPresent, attrs[0].Status)
require.Equal(t, irma.AttributeProofStatusPresent, attrs[1].Status)
attrs, status = manualSessionHelper(t, client, ms, request, "", false)
require.Equal(t, irma.ProofStatusValid, status)
require.Equal(t, irma.AttributeProofStatusExtra, attrs[0].Status)
require.Equal(t, irma.AttributeProofStatusExtra, attrs[1].Status)
test.ClearTestStorage(t)
}
......@@ -157,9 +154,9 @@ func TestManualSessionMultiProof(t *testing.T) {
func TestManualSessionInvalidProof(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\"]}]}"
ms := createManualSessionHandler(t, nil)
result := manualSessionHelper(t, nil, ms, request, request, true)
_, status := manualSessionHelper(t, nil, ms, request, request, true)
require.Equal(t, irma.ProofStatusInvalidCrypto, result.Status)
require.Equal(t, irma.ProofStatusInvalid, status)
test.ClearTestStorage(t)
}
......@@ -167,11 +164,11 @@ func TestManualSessionInvalidProof(t *testing.T) {
func TestManualDisclosureSession(t *testing.T) {
request := "{\"nonce\": 0, \"context\": 0, \"type\": \"disclosing\", \"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
ms := createManualSessionHandler(t, nil)
result := manualSessionHelper(t, nil, ms, request, request, false)
attrs, status := manualSessionHelper(t, nil, ms, request, request, false)
require.Equal(t, irma.AttributeProofStatusPresent, result.Attributes[0].Status)
require.Equal(t, "456", result.Attributes[0].Value["en"])
require.Equal(t, irma.ProofStatusValid, result.Status)
require.Equal(t, irma.AttributeProofStatusPresent, attrs[0].Status)
require.Equal(t, "456", attrs[0].Value["en"])
require.Equal(t, irma.ProofStatusValid, status)
test.ClearTestStorage(t)
}
......@@ -181,13 +178,13 @@ func TestManualDisclosureSessionInvalidRequest(t *testing.T) {
request := "{\"nonce\": 0, \"context\": 0, \"type\": \"disclosing\", \"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
invalidRequest := "{\"nonce\": 0, \"context\": 0, \"type\": \"disclosing\", \"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.university\"]}]}"
ms := createManualSessionHandler(t, nil)
result := manualSessionHelper(t, nil, ms, request, invalidRequest, false)
attrs, status := manualSessionHelper(t, nil, ms, request, invalidRequest, false)
require.Equal(t, irma.ProofStatusMissingAttributes, result.Status)
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, result.Attributes[0].Status)
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, result.Attributes[1].Status)
require.Equal(t, irma.AttributeProofStatusExtra, attrs[1].Status)
test.ClearTestStorage(t)
}
......@@ -80,11 +80,6 @@ var supportedVersions = map[int][]int{
var minVersion = &irma.ProtocolVersion{Major: 2, Minor: supportedVersions[2][0]}
var maxVersion = &irma.ProtocolVersion{Major: 2, Minor: supportedVersions[2][len(supportedVersions[2])-1]}
const (
minVersionHeader = "X-IRMA-MinProtocolVersion"
maxVersionHeader = "X-IRMA-MaxProtocolVersion"
)
// Session constructors
// NewSession starts a new IRMA session, given (along with a handler to pass feedback to) a session request.
......@@ -174,8 +169,8 @@ func (client *Client) newQrSession(qr *irma.Qr, handler Handler) SessionDismisse
return nil
}
session.transport.SetHeader(minVersionHeader, minVersion.String())
session.transport.SetHeader(maxVersionHeader, maxVersion.String())
session.transport.SetHeader(irma.MinVersionHeader, minVersion.String())
session.transport.SetHeader(irma.MaxVersionHeader, maxVersion.String())
if !strings.HasSuffix(session.ServerURL, "/") {
session.ServerURL += "/"
}
......
......@@ -290,11 +290,11 @@ func TestVerifyValidSig(t *testing.T) {
require.Equal(t, sigRequest.Context, big.NewInt(1337))
// Test if we can verify it with the original request
verificationResult := irmaSignedMessage.Verify(conf, sigRequest)
require.Equal(t, verificationResult.Status, ProofStatusValid)
require.Len(t, verificationResult.Attributes, 1)
require.Equal(t, verificationResult.Attributes[0].Status, AttributeProofStatusPresent)
require.Equal(t, verificationResult.Attributes[0].Value["en"], "456")
attrs, status := irmaSignedMessage.Verify(conf, sigRequest)
require.Equal(t, status, ProofStatusValid)
require.Len(t, attrs, 1)
require.Equal(t, attrs[0].Status, AttributeProofStatusPresent)
require.Equal(t, attrs[0].Value["en"], "456")
// Test if we can verify it with a request that contains strings instead of ints for nonce and context
stringRequest := "{\"nonce\": \"42\", \"context\": \"1337\", \"message\":\"I owe you everything\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
......@@ -306,25 +306,25 @@ func TestVerifyValidSig(t *testing.T) {
require.Equal(t, stringSigRequest.Context, big.NewInt(1337))
// Test if we can verify it with the original request
verificationResult = irmaSignedMessage.Verify(conf, sigRequest)
require.Equal(t, verificationResult.Status, ProofStatusValid)
require.Len(t, verificationResult.Attributes, 1)
require.Equal(t, verificationResult.Attributes[0].Status, AttributeProofStatusPresent)
require.Equal(t, verificationResult.Attributes[0].Value["en"], "456")
attrs, status = irmaSignedMessage.Verify(conf, sigRequest)
require.Equal(t, status, ProofStatusValid)
require.Len(t, attrs, 1)
require.Equal(t, attrs[0].Status, AttributeProofStatusPresent)
require.Equal(t, attrs[0].Value["en"], "456")
// Test verify against unmatched request (i.e. different nonce, context or message)
unmatched := "{\"nonce\": 42, \"context\": 1337, \"message\":\"I owe you NOTHING\",\"content\":[{\"label\":\"Student number (RU)\",\"attributes\":[\"irma-demo.RU.studentCard.studentID\"]}]}"
unmatchedSigRequestJSON := []byte(unmatched)
unmatchedSigRequest := &SignatureRequest{}
json.Unmarshal(unmatchedSigRequestJSON, unmatchedSigRequest)
unmatchedResult := irmaSignedMessage.Verify(conf, unmatchedSigRequest)
require.Equal(t, unmatchedResult.Status, ProofStatusUnmatchedRequest)
_, status = irmaSignedMessage.Verify(conf, unmatchedSigRequest)
require.Equal(t, status, ProofStatusUnmatchedRequest)
// Test if we can also verify it without using the original request
verificationResult = irmaSignedMessage.Verify(conf, nil)
require.Equal(t, verificationResult.Status, ProofStatusValid)
require.Len(t, verificationResult.Attributes, 1)
require.Equal(t, verificationResult.Attributes[0].Value["en"], "456")
attrs, status = irmaSignedMessage.Verify(conf, nil)
require.Equal(t, status, ProofStatusValid)
require.Len(t, attrs, 1)
require.Equal(t, attrs[0].Value["en"], "456")
}
func TestVerifyInValidSig(t *testing.T) {
......@@ -340,11 +340,11 @@ func TestVerifyInValidSig(t *testing.T) {
sigRequest := &SignatureRequest{}
json.Unmarshal(sigRequestJSON, sigRequest)
sigProofResult := irmaSignedMessage.Verify(conf, sigRequest)
require.Equal(t, sigProofResult.Status, ProofStatusInvalidCrypto)
_, status := irmaSignedMessage.Verify(conf, sigRequest)
require.Equal(t, status, ProofStatusInvalid)
verificationResult := irmaSignedMessage.Verify(conf, nil)
require.Equal(t, verificationResult.Status, ProofStatusInvalidCrypto)
_, status = irmaSignedMessage.Verify(conf, nil)
require.Equal(t, status, ProofStatusInvalid)
}
func TestVerifyInValidNonce(t *testing.T) {
......@@ -361,11 +361,11 @@ func TestVerifyInValidNonce(t *testing.T) {
sigRequest := &SignatureRequest{}
json.Unmarshal(sigRequestJSON, sigRequest)
sigProofResult := irmaSignedMessage.Verify(conf, sigRequest)
require.Equal(t, sigProofResult.Status, ProofStatusInvalidCrypto)
_, status := irmaSignedMessage.Verify(conf, sigRequest)
require.Equal(t, status, ProofStatusInvalid)
verificationResult := irmaSignedMessage.Verify(conf, nil)
require.Equal(t, verificationResult.Status, ProofStatusInvalidCrypto)
_, status = irmaSignedMessage.Verify(conf, nil)
require.Equal(t, status, ProofStatusInvalid)
}
// Test attribute decoding with both old and new metadata versions
......
......@@ -19,6 +19,11 @@ type Status string
var ForceHttps bool = true
const (
MinVersionHeader = "X-IRMA-MinProtocolVersion"
MaxVersionHeader = "X-IRMA-MaxProtocolVersion"
)
// ProtocolVersion encodes the IRMA protocol version of an IRMA session.
type ProtocolVersion struct {
Major int
......@@ -36,7 +41,7 @@ func (v *ProtocolVersion) String() string {
func (v *ProtocolVersion) UnmarshalJSON(b []byte) (err error) {
var str string
if err := json.Unmarshal(b, &str); err != nil {
return err
str = string(b) // If b is not enclosed by quotes, try it directly
}
parts := strings.Split(str, ".")
if len(parts) != 2 {
......@@ -61,6 +66,21 @@ func (v *ProtocolVersion) Below(major, minor int) bool {
return v.Major == major && v.Minor < minor
}
func (v *ProtocolVersion) BelowVersion(other *ProtocolVersion) bool {
return v.Below(other.Major, other.Minor)
}
func (v *ProtocolVersion) Above(major, minor int) bool {
if v.Major > major {
return true
}
return v.Major == major && v.Minor > minor
}
func (v *ProtocolVersion) AboveVersion(other *ProtocolVersion) bool {
return v.Above(other.Major, other.Minor)
}
// GetMetadataVersion maps a chosen protocol version to a metadata version that
// the server will use.
func GetMetadataVersion(v *ProtocolVersion) byte {
......
......@@ -5,6 +5,7 @@ import (
"io/ioutil"
"math/big"
"strconv"
"strings"
"time"
"encoding/json"
......@@ -130,6 +131,7 @@ type IdentityProviderJwt struct {
// SessionRequest is an IRMA session.
type SessionRequest interface {
Validator
GetNonce() *big.Int
SetNonce(*big.Int)
GetContext() *big.Int
......@@ -154,20 +156,13 @@ func (cr *CredentialRequest) Info(conf *Configuration, metadataVersion byte) (*C
return NewCredentialInfo(list.Ints, conf), nil
}
// AttributeList returns the list of attributes from this credential request.
func (cr *CredentialRequest) AttributeList(conf *Configuration, metadataVersion byte) (*AttributeList, error) {
meta := NewMetadataAttribute(metadataVersion)
meta.setKeyCounter(cr.KeyCounter)
meta.setCredentialTypeIdentifier(cr.CredentialTypeID.String())
meta.setSigningDate()
err := meta.setExpiryDate(cr.Validity)
if err != nil {
return nil, err
}
// Validate checks that this credential request is consistent with the specified Configuration:
// the credential type is known, all required attributes are present and no unknown attributes
// are given.
func (cr *CredentialRequest) Validate(conf *Configuration) error {
credtype := conf.CredentialTypes[*cr.CredentialTypeID]
if credtype == nil {
return nil, errors.New("Unknown credential type")
return errors.New("Credential request of unknown credential type")
}
// Check that there are no attributes in the credential request that aren't
......@@ -181,10 +176,36 @@ func (cr *CredentialRequest) AttributeList(conf *Configuration, metadataVersion
}
}
if !found {
return nil, errors.New("Unknown CR attribute")
return errors.New("Credential request contaiins unknown attribute")
}
}
for _, attrtype := range credtype.Attributes {
if _, present := cr.Attributes[attrtype.ID]; !present && attrtype.Optional != "true" {
return errors.New("Required attribute not present in credential request")
}
}
return nil
}
// AttributeList returns the list of attributes from this credential request.
func (cr *CredentialRequest) AttributeList(conf *Configuration, metadataVersion byte) (*AttributeList, error) {
if err := cr.Validate(conf); err != nil {
return nil, err
}
// Compute metadata attribute
meta := NewMetadataAttribute(metadataVersion)
meta.setKeyCounter(cr.KeyCounter)
meta.setCredentialTypeIdentifier(cr.CredentialTypeID.String())
meta.setSigningDate()
if err := meta.setExpiryDate(cr.Validity); err != nil {
return nil, err
}
// Compute other attributes
credtype := conf.CredentialTypes[*cr.CredentialTypeID]
attrs := make([]*big.Int, len(credtype.Attributes)+1)
attrs[0] = meta.Int
for i, attrtype := range credtype.Attributes {
......@@ -196,10 +217,6 @@ func (cr *CredentialRequest) AttributeList(conf *Configuration, metadataVersion
attrs[i+1].Lsh(attrs[i+1], 1) // attr <<= 1
attrs[i+1].Add(attrs[i+1], big.NewInt(1)) // attr += 1
}
} else {
if attrtype.Optional != "true" {
return nil, errors.New("Required attribute not provided")
}
}
}
......@@ -345,7 +362,7 @@ func (sr *SignatureRequest) GetNonce() *big.Int {
// Convert fields in JSON string to BigInterger if they are string
// Supply fieldnames as a slice as second argument
func convertFieldsToBigInt(jsonString []byte, fieldNames []string) ([]byte, error) {
var rawRequest map[string]interface{}
var rawRequest map[string]json.RawMessage
err := json.Unmarshal(jsonString, &rawRequest)
if err != nil {
......@@ -353,10 +370,8 @@ func convertFieldsToBigInt(jsonString []byte, fieldNames []string) ([]byte, erro
}
for _, fieldName := range fieldNames {
field := new(big.Int)
fieldString := fmt.Sprintf("%v", rawRequest[fieldName])
field.SetString(fieldString, 10)
rawRequest[fieldName] = field
fieldString := string(rawRequest[fieldName])
rawRequest[fieldName] = []byte(strings.Trim(fieldString, "\""))
}
return json.Marshal(rawRequest)
......
......@@ -16,7 +16,7 @@ type AttributeProofStatus string
const (
ProofStatusValid = ProofStatus("VALID") // Proof is valid
ProofStatusInvalidCrypto = ProofStatus("INVALID_CRYPTO") // Proof is invalid
ProofStatusInvalid = ProofStatus("INVALID") // Proof is invalid
ProofStatusInvalidTimestamp = ProofStatus("INVALID_TIMESTAMP") // Attribute-based signature had invalid timestamp
ProofStatusUnmatchedRequest = ProofStatus("UNMATCHED_REQUEST") // Proof does not correspond to a specified request
ProofStatusMissingAttributes = ProofStatus("MISSING_ATTRIBUTES") // Proof does not contain all requested attributes
......@@ -47,10 +47,10 @@ type DisclosedAttribute struct {
// ProofList is a gabi.ProofList with some extra methods.
type ProofList gabi.ProofList
// extractPublicKeys returns the public keys of each proof in the proofList, in the same order,
// ExtractPublicKeys returns the public keys of each proof in the proofList, in the same order,
// for later use in verification of the proofList. If one of the proofs is not a ProofD
// an error is returned.
func (pl ProofList) extractPublicKeys(configuration *Configuration) ([]*gabi.PublicKey, error) {
func (pl ProofList) ExtractPublicKeys(configuration *Configuration) ([]*gabi.PublicKey, error) {
var publicKeys = make([]*gabi.PublicKey, 0, len(pl))
for _, v := range pl {
......@@ -71,18 +71,47 @@ func (pl ProofList) extractPublicKeys(configuration *Configuration) ([]*gabi.Pub
}
// VerifyProofs verifies the proofs cryptographically.
func (pl ProofList) VerifyProofs(configuration *Configuration, context *big.Int, nonce *big.Int, isSig bool) bool {
// Extract public keys
pks, err := pl.extractPublicKeys(configuration)
if err != nil {
func (pl ProofList) VerifyProofs(configuration *Configuration, context *big.Int, nonce *big.Int, publickeys []*gabi.PublicKey, isSig bool) bool {
if publickeys == nil {
var err error
publickeys, err = pl.ExtractPublicKeys(configuration)
if err != nil {
return false
}
}
if len(pl) != len(publickeys) {
return false
}
return gabi.ProofList(pl).Verify(pks, context, nonce, true, isSig)
// If the secret key comes from a credential whose scheme manager has a keyshare server,
// then the secretkey = userpart + keysharepart.
// So, we can only expect two secret key responses to be equal if their credentials
// are both associated to either no keyshare server, or the same keyshare server.
// (We have to check this here instead of in gabi, because gabi is unaware of schemes
// and whether or not they are distributed.)
secretkeyResponses := make(map[SchemeManagerIdentifier]*big.Int)
nonKssSchemeID := NewSchemeManagerIdentifier(".") // We use this id for all schemes that don't use a kss
for i, proof := range pl {
schemeID := NewIssuerIdentifier(publickeys[i].Issuer).SchemeManagerIdentifier()
if !configuration.SchemeManagers[schemeID].Distributed() {
schemeID = nonKssSchemeID
}
if response, contains := secretkeyResponses[schemeID]; !contains {
secretkeyResponses[schemeID] = proof.SecretKeyResponse()
} else {
if response.Cmp(proof.SecretKeyResponse()) != 0 {
return false
}
}
}
return gabi.ProofList(pl).Verify(publickeys, context, nonce, isSig)
}
// Expired returns true if any of the contained disclosure proofs is specified at the specified time,
// or now, when the specified time is nil.
func (pl ProofList) Expired(configuration *Configuration, t *time.Time) (bool, error) {
func (pl ProofList) Expired(configuration *Configuration, t *time.Time) bool {
if t == nil {
temp := time.Now()
t = &temp
......@@ -90,14 +119,14 @@ func (pl ProofList) Expired(configuration *Configuration, t *time.Time) (bool, e
for _, proof := range pl {
proofd, ok := proof.(*gabi.ProofD)
if !ok {
return false, errors.New("ProofList contained a proof that was not a disclosure proof")
continue
}
metadata := MetadataFromInt(proofd.ADisclosed[1], configuration) // index 1 is metadata attribute
if metadata.Expiry().Before(*t) {
return true, nil
return true
}
}
return false, nil
return false
}
// DisclosedAttributes returns a slice containing the disclosed attributes that are present in the proof list.
......@@ -172,33 +201,40 @@ func (pl ProofList) DisclosedAttributes(configuration *Configuration, disjunctio
return len(disjunctions) == 0 || disjunctions.satisfied(), list, nil
}
func (pl ProofList) verifyAgainstDisjunctions(configuration *Configuration, required AttributeDisjunctionList, nonce, context *big.Int, issig bool) ([]*DisclosedAttribute, ProofStatus) {
func (pl ProofList) VerifyAgainstDisjunctions(
configuration *Configuration,
required AttributeDisjunctionList,
context, nonce *big.Int,
publickeys []*gabi.PublicKey,
issig bool,
) ([]*DisclosedAttribute, ProofStatus) {
// Cryptographically verify the IRMA disclosure proofs in the signature
if !pl.VerifyProofs(configuration, nonce, context, issig) {
return nil, ProofStatusInvalidCrypto
if !pl.VerifyProofs(configuration, context, nonce, publickeys, issig) {
return nil, ProofStatusInvalid
}
// Next extract the contained attributes from the proofs, and match them to the signature request if present
allmatched, list, err := pl.DisclosedAttributes(configuration, required)
if err != nil {
return nil, ProofStatusInvalidCrypto
return nil, ProofStatusInvalid
}
// Return MISSING_ATTRIBUTES as proofstatus if one of the disjunctions in the request (if present) is not satisfied
// This status takes priority over 'EXPIRED'
if !allmatched {
return list, ProofStatusMissingAttributes
}
now := time.Now()
if expired := pl.Expired(configuration, &now); expired {
return list, ProofStatusExpired
}
return list, ProofStatusValid
}
func (pl ProofList) Verify(configuration *Configuration, request *DisclosureRequest) *VerificationResult {
list, status := pl.verifyAgainstDisjunctions(configuration, request.Content, request.Nonce, request.Context, false)
return &VerificationResult{
Attributes: list,
Status: status,
}
func (pl ProofList) Verify(configuration *Configuration, request *DisclosureRequest) ([]*DisclosedAttribute, ProofStatus) {
return pl.VerifyAgainstDisjunctions(configuration, request.Content, request.Context, request.Nonce, nil, false)
}
// Verify the attribute-based signature, optionally against a corresponding signature request. If the request is present
......@@ -210,16 +246,14 @@ func (pl ProofList) Verify(configuration *Configuration, request *DisclosureRequ
//
// The signature request is optional; if it is nil then the attribute-based signature is still verified, and all