Skip to content

Commit

Permalink
refactor verify cred subj (#68)
Browse files Browse the repository at this point in the history
* reuse parseQueriesMetadata
  • Loading branch information
volodymyr-basiuk authored Feb 21, 2024
1 parent 484da29 commit 83b612e
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 151 deletions.
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

0 comments on commit 83b612e

Please sign in to comment.