Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor verify cred subj #68

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 26 additions & 12 deletions pubsignals/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func ParseCredentialSubject(_ context.Context, credentialSubject any) (out []Pro
if credentialSubject == nil {
return []PropertyQuery{
{
Operator: circuits.EQ,
Operator: circuits.NOOP,
FieldName: "",
},
}, nil
Expand All @@ -58,16 +58,11 @@ func ParseCredentialSubject(_ context.Context, credentialSubject any) (out []Pro
return nil, errors.New("Failed to convert credential subject to JSONObject")
}

if len(jsonObject) == 0 {
return []PropertyQuery{
{
Operator: circuits.EQ,
FieldName: "",
},
}, nil
}
for fieldName, fieldReq := range jsonObject {
fieldReqEntries := fieldReq.(map[string]interface{})
fieldReqEntries, ok := fieldReq.(map[string]interface{})
if !ok {
return nil, errors.New("failed cast type map[string]interface")
}
isSelectiveDisclosure := len(fieldReqEntries) == 0

if isSelectiveDisclosure {
Expand Down Expand Up @@ -150,7 +145,8 @@ func ParseQueryMetadata(ctx context.Context, propertyQuery PropertyQuery, ldCont

if propertyQuery.OperatorValue != nil {
if !IsValidOperation(datatype, propertyQuery.Operator) {
return nil, fmt.Errorf("operator %d is not supported for datatype %s", propertyQuery.Operator, datatype)
operatorName, _ := getKeyByValue(circuits.QueryOperators, propertyQuery.Operator)
return nil, fmt.Errorf("invalid operation '%s' for field type '%s'", operatorName, datatype)
}
}

Expand Down Expand Up @@ -186,15 +182,24 @@ func transformQueryValueToBigInts(_ context.Context, value any, ldType string) (
out[i] = big.NewInt(0)
}

if reflect.TypeOf(value).Kind() == reflect.Array {
if value == nil {
return out, nil
}
if reflect.TypeOf(value).Kind() == reflect.Array || reflect.TypeOf(value).Kind() == reflect.Slice {
v := reflect.ValueOf(value)
for i := 0; i < v.Len(); i++ {
if !isPositiveInteger(v) {
return nil, ErrNegativeValue
}
out[i], err = merklize.HashValue(ldType, v.Index(i).Interface())
if err != nil {
return nil, err
}
}
} else {
if !isPositiveInteger(value) {
return nil, ErrNegativeValue
}
out[0], err = merklize.HashValue(ldType, value)
if err != nil {
return nil, err
Expand Down Expand Up @@ -243,3 +248,12 @@ func getSerializationAttrFromParsedContext(ldCtx *ld.Context,

return "", nil
}

func getKeyByValue(m map[string]int, targetValue int) (string, bool) {
for key, value := range m {
if value == targetValue {
return key, true
}
}
return "", false
}
123 changes: 23 additions & 100 deletions pubsignals/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,59 +229,48 @@ func (q Query) verifyCredentialSubject(
schemaLoader ld.DocumentLoader,
supportSdOperator bool,
) error {
fieldName, predicate, err := extractQueryFields(q.CredentialSubject)

ctx := context.Background()

queriesMetadata, err := ParseQueriesMetadata(ctx, q.Type, string(ctxBytes), q.CredentialSubject, merklize.Options{DocumentLoader: schemaLoader})
if err != nil {
return err
}

var fieldType string
if fieldName != "" {
fieldType, err = merklize.Options{DocumentLoader: schemaLoader}.
TypeFromContext(ctxBytes, fmt.Sprintf("%s.%s", q.Type, fieldName))
if err != nil {
return err
if len(queriesMetadata) > 1 {
if queriesMetadata[0].FieldName == queriesMetadata[1].FieldName {
return errors.New("multiple predicates for one field not supported")
}
return errors.New("multiple requests not supported")
}

var metadata QueryMetadata

if len(queriesMetadata) == 1 {
metadata = queriesMetadata[0]
}

// validate selectivity disclosure request
if q.isSelectivityDisclosure(predicate) {
ctx := context.Background()
return q.validateDisclosure(ctx, pubSig, fieldName,
if metadata.Operator == circuits.SD {
return q.validateDisclosure(ctx, pubSig, metadata.FieldName,
verifiablePresentation, schemaLoader, supportSdOperator)
}

// validate empty credential subject request
if q.isEmptyCredentialSubject(predicate, pubSig.Merklized) {
if q.isEmptyCredentialSubject(metadata.Operator, pubSig.Merklized) {
return q.verifyEmptyCredentialSubject(pubSig)
}

values, operator, err := parseFieldPredicate(fieldType, predicate)
if err != nil {
return err
}

if operator != pubSig.Operator {
if metadata.Operator != pubSig.Operator {
return ErrRequestOperator
}

if operator == circuits.NOOP {
if metadata.Operator == circuits.NOOP {
return nil
}

if len(values) > len(pubSig.Value) {
return ErrValuesSize
}

if len(values) < pubSig.ValueArraySize {
diff := pubSig.ValueArraySize - len(values)
for diff > 0 {
values = append(values, big.NewInt(0))
diff--
}
}

for i := 0; i < len(values); i++ {
if values[i].Cmp(pubSig.Value[i]) != 0 {
for i := 0; i < len(metadata.Values); i++ {
if metadata.Values[i].Cmp(pubSig.Value[i]) != 0 {
return ErrInvalidValues
}
}
Expand Down Expand Up @@ -387,46 +376,11 @@ func (q Query) verifyEmptyCredentialSubject(
return nil
}

func (q Query) isSelectivityDisclosure(
predicate map[string]interface{}) bool {
return q.CredentialSubject != nil && len(predicate) == 0
}

func (q Query) isEmptyCredentialSubject(
predicate map[string]interface{},
operator,
isMerklized int,
) bool {
return q.CredentialSubject == nil && len(predicate) == 0 && isMerklized == 1
}

func parseFieldPredicate(
fieldType string,
fieldPredicate map[string]interface{},
) (
values []*big.Int,
operator int,
err error,
) {
for op, v := range fieldPredicate {
var ok bool
operator, ok = circuits.QueryOperators[op]
if !ok {
return nil, 0, errors.New("query operator is not supported")
}

if !IsValidOperation(fieldType, operator) {
return nil, 0, errors.Errorf("invalid operation '%s' for field type '%s'", op, fieldType)
}

values, err = getValuesAsArray(v, fieldType)
if err != nil {
return nil, 0, err
}

// only one predicate for field is supported
break
}
return values, operator, nil
return q.CredentialSubject == nil && operator == circuits.NOOP && isMerklized == 1
}

func extractQueryFields(req map[string]interface{}) (fieldName string, fieldPredicate map[string]interface{}, err error) {
Expand All @@ -450,37 +404,6 @@ func extractQueryFields(req map[string]interface{}) (fieldName string, fieldPred
return fieldName, fieldPredicate, nil
}

func getValuesAsArray(v interface{}, valueType string) ([]*big.Int, error) {
var values []*big.Int

listOfValues, ok := v.([]interface{})
if ok {
values = make([]*big.Int, len(listOfValues))
for i, item := range listOfValues {
if !isPositiveInteger(item) {
return nil, ErrNegativeValue
}
hashedValue, err := merklize.HashValue(valueType, item)
if err != nil {
return nil, err
}
values[i] = hashedValue
}
return values, nil
}

if !isPositiveInteger(v) {
return nil, ErrNegativeValue
}
hashedValue, err := merklize.HashValue(valueType, v)
if err != nil {
return nil, err
}
values = append(values, hashedValue)

return values, nil
}

func isPositiveInteger(v interface{}) bool {
number, err := strconv.ParseFloat(fmt.Sprintf("%v", v), 64)
if err != nil {
Expand Down
Loading
Loading