Skip to content

Commit

Permalink
add link id checks
Browse files Browse the repository at this point in the history
  • Loading branch information
volodymyr-basiuk committed Jan 31, 2024
1 parent cf901a5 commit 99d382e
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 48 deletions.
129 changes: 117 additions & 12 deletions auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,81 @@ func CreateContractInvokeRequestWithMessage(
}
}

func verifyAuthRequest(request protocol.AuthorizationRequestMessage) error {
groupIDValidationMap := make(map[int][]protocol.ZeroKnowledgeProofRequest)

for _, proofRequest := range request.Body.Scope {
proofRequestQuery, err := unmarshalQuery(proofRequest.Query)
if err != nil {
return err
}
groupID := proofRequestQuery.GroupID
if groupID != 0 {
existingRequests := groupIDValidationMap[groupID]

// Validate that all requests in the group have the same schema, issuer, and circuit
for _, existingRequest := range existingRequests {
existingRequestQuery, err := unmarshalQuery(existingRequest.Query)
if err != nil {
return err
}
if existingRequestQuery.Type != proofRequestQuery.Type {
return errors.New("all requests in the group should have the same type")
}

if existingRequestQuery.Context != proofRequestQuery.Context {
return errors.New("all requests in the group should have the same context")
}

allowedIssuers := proofRequestQuery.AllowedIssuers
existingRequestAllowedIssuers := existingRequestQuery.AllowedIssuers
if !checkIssuersEquality(allowedIssuers, existingRequestAllowedIssuers) {
return errors.New("all requests in the group should have the same issuer")
}
}

groupIDValidationMap[groupID] = append(existingRequests, proofRequest)
}
}

return nil
}

func unmarshalQuery(queryMap map[string]interface{}) (out pubsignals.Query, err error) {
// prepare query from request
queryBytes, err := json.Marshal(queryMap)
if err != nil {
return out, err
}
err = json.Unmarshal(queryBytes, &out)
if err != nil {
return out, err
}
return out, nil
}

func checkIssuersEquality(issuers1, issuers2 []string) bool {
if len(issuers1) != len(issuers2) {
return false
}

for _, issuer := range issuers1 {
found := false
for _, existingIssuer := range issuers2 {
if issuer == existingIssuer || existingIssuer == "*" {
found = true
break
}
}

if !found {
return false
}
}

return true
}

// VerifyAuthResponse performs verification of auth response based on auth request
func (v *Verifier) VerifyAuthResponse(
ctx context.Context,
Expand All @@ -326,7 +401,20 @@ func (v *Verifier) VerifyAuthResponse(
return errors.Errorf("sender of the request is not a target of response - expected %s, given %s", request.From, response.To)
}

err := verifyAuthRequest(request)
if err != nil {
return err
}

groupIDToLinkIDMap := make(map[int][]map[string]*big.Int)
for _, proofRequest := range request.Body.Scope {
// prepare query from request
query, err := unmarshalQuery(proofRequest.Query)
if err != nil {
return err
}
groupID := query.GroupID

proofResponse := findProofByRequestID(response.Body.Scope, proofRequest.ID)
if proofResponse == nil {
return errors.Errorf("proof for zk request id %v is presented not found", proofRequest.ID)
Expand All @@ -349,17 +437,6 @@ func (v *Verifier) VerifyAuthResponse(
return errors.Wrap(err, fmt.Sprintf("circuit with id %s is not supported by library", proofRequest.CircuitID))
}

// prepare query from request
queryBytes, err := json.Marshal(proofRequest.Query)
if err != nil {
return err
}
var query pubsignals.Query
err = json.Unmarshal(queryBytes, &query)
if err != nil {
return err
}

// verify proof author

err = cv.VerifyIDOwnership(response.From, big.NewInt(int64(proofResponse.ID)))
Expand All @@ -384,7 +461,7 @@ func (v *Verifier) VerifyAuthResponse(
}
proofRequest.Params[pubsignals.ParamNameVerifierDID] = verifierDID

err = cv.VerifyQuery(ctx, query, v.documentLoader, rawMessage, proofRequest.Params, opts...)
pubSignals, err := cv.VerifyQuery(ctx, query, v.documentLoader, rawMessage, proofRequest.Params, opts...)
if err != nil {
return err
}
Expand All @@ -394,6 +471,34 @@ func (v *Verifier) VerifyAuthResponse(
return err
}

if response.From == "" {
return errors.Errorf("proof response doesn't contain from field")
}

if pubSignals.LinkID != nil && groupID != 0 {
if existingLinks, exists := groupIDToLinkIDMap[groupID]; exists {
linkIDMap := map[string]*big.Int{"linkID": pubSignals.LinkID, "requestID": new(big.Int).SetUint64(uint64(proofResponse.ID))}
groupIDToLinkIDMap[groupID] = append(existingLinks, linkIDMap)
} else {
linkIDMap := map[string]*big.Int{"linkID": pubSignals.LinkID, "requestID": new(big.Int).SetUint64(uint64(proofResponse.ID))}
groupIDToLinkIDMap[groupID] = []map[string]*big.Int{linkIDMap}
}
}
if groupID != 0 {
// verify grouping links
for groupId, metas := range groupIDToLinkIDMap {
// Check that all linkIDs are the same
if len(metas) > 1 {
firstLinkID := metas[0]["linkID"]
for _, meta := range metas[1:] {
if meta["linkID"].Cmp(firstLinkID) != 0 {
return errors.Errorf("Link id validation failed for group %d, request linkID to requestIds info: %v", groupId, metas)
}
}
}
}
}

}

return nil
Expand Down
1 change: 0 additions & 1 deletion auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1154,7 +1154,6 @@ func TestFullVerifyLinkedProofsVerification(t *testing.T) {
opt := false
mtpProofRequest1.Optional = &opt
mtpProofRequest1.Query = map[string]interface{}{
"groupId": 1,
"allowedIssuers": []string{"*"},
"credentialSubject": map[string]interface{}{
"documentType": map[string]interface{}{
Expand Down
9 changes: 6 additions & 3 deletions pubsignals/atomicMtpV2.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ func (c *AtomicQueryMTPV2) VerifyQuery(
verifiablePresentation json.RawMessage,
_ map[string]interface{},
opts ...VerifyOpt,
) error {
return query.Check(ctx, schemaLoader, &CircuitOutputs{
) (CircuitOutputs, error) {
outputs := CircuitOutputs{
IssuerID: c.IssuerID,
ClaimSchema: c.ClaimSchema,
SlotIndex: c.SlotIndex,
Expand All @@ -40,7 +40,10 @@ func (c *AtomicQueryMTPV2) VerifyQuery(
ClaimPathNotExists: c.ClaimPathNotExists,
ValueArraySize: c.ValueArraySize,
IsRevocationChecked: c.IsRevocationChecked,
}, verifiablePresentation, false, opts...)
}

err := query.Check(ctx, schemaLoader, &outputs, verifiablePresentation, false, opts...)
return outputs, err
}

// VerifyStates verifies user state and issuer claim issuance state in the smart contract.
Expand Down
10 changes: 4 additions & 6 deletions pubsignals/atomicSigV2.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ func (c *AtomicQuerySigV2) VerifyQuery(
verifiablePresentation json.RawMessage,
_ map[string]interface{},
opts ...VerifyOpt,
) error {
err := query.Check(ctx, schemaLoader, &CircuitOutputs{
) (CircuitOutputs, error) {
outputs := CircuitOutputs{
IssuerID: c.IssuerID,
ClaimSchema: c.ClaimSchema,
SlotIndex: c.SlotIndex,
Expand All @@ -40,11 +40,9 @@ func (c *AtomicQuerySigV2) VerifyQuery(
ClaimPathNotExists: c.ClaimPathNotExists,
ValueArraySize: c.ValueArraySize,
IsRevocationChecked: c.IsRevocationChecked,
}, verifiablePresentation, false, opts...)
if err != nil {
return err
}
return nil
err := query.Check(ctx, schemaLoader, &outputs, verifiablePresentation, false, opts...)
return outputs, err
}

// VerifyStates verifies user state and issuer auth claim state in the smart contract.
Expand Down
29 changes: 15 additions & 14 deletions pubsignals/atomicV3.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ func (c *AtomicQueryV3) VerifyQuery(
verifiablePresentation json.RawMessage,
params map[string]interface{},
opts ...VerifyOpt,
) error {
err := query.Check(ctx, schemaLoader, &CircuitOutputs{
) (CircuitOutputs, error) {
outputs := CircuitOutputs{
IssuerID: c.IssuerID,
ClaimSchema: c.ClaimSchema,
SlotIndex: c.SlotIndex,
Expand All @@ -48,20 +48,21 @@ func (c *AtomicQueryV3) VerifyQuery(
OperatorOutput: c.OperatorOutput,
Nullifier: c.Nullifier,
ProofType: c.ProofType,
}, verifiablePresentation, true, opts...)
}
err := query.Check(ctx, schemaLoader, &outputs, verifiablePresentation, true, opts...)
if err != nil {
return err
return outputs, err
}

// V3 NEW
switch query.ProofType {
case string(verifiable.BJJSignatureProofType):
if c.ProofType != 1 {
return ErrWronProofType
return outputs, ErrWronProofType
}
case string(verifiable.Iden3SparseMerkleTreeProofType):
if c.ProofType != 2 {
return ErrWronProofType
return outputs, ErrWronProofType
}
default:
}
Expand All @@ -71,35 +72,35 @@ func (c *AtomicQueryV3) VerifyQuery(
if ok {
verifierDID, ok := params[ParamNameVerifierDID].(*w3c.DID)
if !ok {
return errors.New("verifier did is mandatory if nullifier session is set in the request")
return outputs, errors.New("verifier did is mandatory if nullifier session is set in the request")
}
id, err := core.IDFromDID(*verifierDID)
if err != nil {
return err
return outputs, err
}
if c.VerifierID.BigInt().Cmp(id.BigInt()) != 0 {
return errors.New("wrong verifier is used for nullification")
return outputs, errors.New("wrong verifier is used for nullification")
}

nullifierSessionID, ok := new(big.Int).SetString(nullifierSessionIDparam, 10)
if !ok {
return errors.New("nullifier session is not a valid big integer")
return outputs, errors.New("nullifier session is not a valid big integer")
}
if c.NullifierSessionID.Cmp(nullifierSessionID) != 0 {
return errors.Errorf("wrong verifier session id is used for nullification: expected %s given %s,", nullifierSessionID.String(), c.NullifierSessionID.String())
return outputs, errors.Errorf("wrong verifier session id is used for nullification: expected %s given %s,", nullifierSessionID.String(), c.NullifierSessionID.String())
}
} else if c.NullifierSessionID != nil && c.NullifierSessionID.Int64() != 0 {
// if no nullifierSessionID in params - we need to verify that nullifier is zero
return errors.New("nullifier id is generated but wasn't requested")
return outputs, errors.New("nullifier id is generated but wasn't requested")
}

}

if query.GroupID != 0 && c.LinkID == nil {
return errors.New("proof doesn't contain link id, but group id is provided")
return outputs, errors.New("proof doesn't contain link id, but group id is provided")
}

return nil
return outputs, nil
}

// VerifyStates verifies user state and issuer auth claim state in the smart contract.
Expand Down
4 changes: 2 additions & 2 deletions pubsignals/authV2.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ func (c *AuthV2) VerifyQuery(
_ ld.DocumentLoader,
_ json.RawMessage,
_ map[string]interface{},
_ ...VerifyOpt) error {
return errors.New("authV2 circuit doesn't support queries")
_ ...VerifyOpt) (CircuitOutputs, error) {
return CircuitOutputs{}, errors.New("authV2 circuit doesn't support queries")
}

// VerifyStates verify AuthV2 tests.
Expand Down
2 changes: 1 addition & 1 deletion pubsignals/circuitsVerifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type StateResolver interface {

// Verifier is interface for verification of public signals of zkp
type Verifier interface {
VerifyQuery(ctx context.Context, query Query, schemaLoader ld.DocumentLoader, verifiablePresentation json.RawMessage, circuitParams map[string]interface{}, opts ...VerifyOpt) error
VerifyQuery(ctx context.Context, query Query, schemaLoader ld.DocumentLoader, verifiablePresentation json.RawMessage, circuitParams map[string]interface{}, opts ...VerifyOpt) (CircuitOutputs, error)
VerifyStates(ctx context.Context, resolvers map[string]StateResolver, opts ...VerifyOpt) error
VerifyIDOwnership(userIdentifier string, challenge *big.Int) error

Expand Down
24 changes: 15 additions & 9 deletions pubsignals/linkedMultiQuery.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,40 +26,41 @@ func (c *LinkedMultiQuery) VerifyQuery(
verifiablePresentation json.RawMessage,

Check failure on line 26 in pubsignals/linkedMultiQuery.go

View workflow job for this annotation

GitHub Actions / lint

unused-parameter: parameter 'verifiablePresentation' seems to be unused, consider removing or renaming it as _ (revive)
params map[string]interface{},
opts ...VerifyOpt,
) error {
) (CircuitOutputs, error) {
var outputs CircuitOutputs
schemaDoc, err := schemaLoader.LoadDocument(query.Context)
if err != nil {
return fmt.Errorf("failed load schema by context: %w", err)
return outputs, fmt.Errorf("failed load schema by context: %w", err)
}

schemaBytes, err := json.Marshal(schemaDoc.Document)
if err != nil {
return fmt.Errorf("failed jsonify schema document: %w", err)
return outputs, fmt.Errorf("failed jsonify schema document: %w", err)
}

schemaID, err := merklize.Options{DocumentLoader: schemaLoader}.
TypeIDFromContext(schemaBytes, query.Type)
if err != nil {
return err
return outputs, err
}
schemaHash := utils.CreateSchemaHash([]byte(schemaID))

if schemaHash.BigInt() == nil {
return fmt.Errorf("query schema error")
return outputs, fmt.Errorf("query schema error")
}

merkOption := merklize.Options{
DocumentLoader: schemaLoader,
}
queriesMetadata, err := ParseQueriesMetadata(ctx, query.Type, string(schemaBytes), query.CredentialSubject, merkOption)
if err != nil {
return err
return outputs, err
}

for i := 0; i < len(queriesMetadata); i++ {
valueHash, err := poseidon.SpongeHashX(queriesMetadata[i].Values, 6)
if err != nil {
return err
return outputs, err
}

merklizedSchema := big.NewInt(0)
Expand All @@ -76,11 +77,16 @@ func (c *LinkedMultiQuery) VerifyQuery(
})

if c.CircuitQueryHash[i].Cmp(queryHash) != 0 {
return fmt.Errorf("query hashes do not match")
return outputs, fmt.Errorf("query hashes do not match")
}
}

return nil
outputs = CircuitOutputs{
LinkID: c.LinkID,
Merklized: c.Merklized,
}

return outputs, nil
}

// VerifyStates verifies user state and issuer auth claim state in the smart contract.
Expand Down

0 comments on commit 99d382e

Please sign in to comment.