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

Signature / proof verification #84

Merged
merged 80 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from 65 commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
b710ce3
init ValidateProof func
volodymyr-basiuk Dec 20, 2023
13bb8d7
add VerifyPoseidon
volodymyr-basiuk Dec 21, 2023
4456b0f
check issuer state
volodymyr-basiuk Dec 21, 2023
79ab1b8
add get latest state
volodymyr-basiuk Dec 22, 2023
1e1c802
check genesis
volodymyr-basiuk Dec 22, 2023
b3c0e97
check RootFromProof
volodymyr-basiuk Dec 22, 2023
1594234
fix comment
volodymyr-basiuk Dec 22, 2023
d899488
rename validate method
volodymyr-basiuk Jan 3, 2024
9794791
fix linter
volodymyr-basiuk Jan 3, 2024
4037c49
add credential status validation for BJJ proof
volodymyr-basiuk Jan 3, 2024
3873dbb
move eth client to config (1 instanse for validate call)
volodymyr-basiuk Jan 4, 2024
71634ac
go 1.18
volodymyr-basiuk Jan 4, 2024
16f9b6a
fix unit tests
volodymyr-basiuk Jan 4, 2024
55033c9
go 1.18
volodymyr-basiuk Jan 4, 2024
a9bf675
fix unit tests (2)
volodymyr-basiuk Jan 4, 2024
3f3f732
go 1.20
volodymyr-basiuk Jan 4, 2024
13e5162
skip 1.18 container
volodymyr-basiuk Jan 4, 2024
197015c
fix linter
volodymyr-basiuk Jan 4, 2024
e8ef955
fix linter
volodymyr-basiuk Jan 4, 2024
03a2860
update to pilygonid resolver
volodymyr-basiuk Jan 4, 2024
709f76e
encapsulate did if state in params
volodymyr-basiuk Jan 4, 2024
134bb8f
fix linter
volodymyr-basiuk Jan 4, 2024
82a14fb
mock endpoints
volodymyr-basiuk Jan 5, 2024
0cc7ec3
revert CredentialStatus type changes
volodymyr-basiuk Jan 5, 2024
4644326
add credentialStatusResolver
volodymyr-basiuk Jan 5, 2024
4b8bdc6
upgrade core, reuse chainID
volodymyr-basiuk Jan 5, 2024
8050c0d
remove go-ether
volodymyr-basiuk Jan 5, 2024
d9faae3
Merge branch 'main' into feature/validate-cred-proof
volodymyr-basiuk Jan 5, 2024
59be582
fix lint & return go 1.18
volodymyr-basiuk Jan 5, 2024
d330714
return proof valid
volodymyr-basiuk Jan 8, 2024
02def93
add support to Iden3commRevocationStatusV1
volodymyr-basiuk Jan 8, 2024
f1b0a64
fix linter
volodymyr-basiuk Jan 8, 2024
0d9402c
split status check by separate files
volodymyr-basiuk Jan 8, 2024
fe71e5b
separate direct status
volodymyr-basiuk Jan 8, 2024
89ce10e
add agent status unit test
volodymyr-basiuk Jan 8, 2024
d8c4627
fix linter
volodymyr-basiuk Jan 8, 2024
22b8404
change err messages
volodymyr-basiuk Jan 10, 2024
8ae5447
rename CredStatusConfig to W3CProofVerificationOpt
volodymyr-basiuk Jan 10, 2024
c04426f
refactor with CredentialStatusResolverRegistry
volodymyr-basiuk Jan 11, 2024
df9c60d
refactor resolve & opts
volodymyr-basiuk Jan 12, 2024
ff4c896
return RevocationStatus from resolver (not MTPProof)
volodymyr-basiuk Jan 12, 2024
744c0ef
fix linter
volodymyr-basiuk Jan 12, 2024
588f5e2
cleanup
volodymyr-basiuk Jan 12, 2024
1ae002a
public config fields
volodymyr-basiuk Jan 12, 2024
ab00cca
move onChainRevStatus
volodymyr-basiuk Jan 12, 2024
8b3a33d
rm unused struct
volodymyr-basiuk Jan 12, 2024
b771bd5
upgrade merkletree-proof to v4
volodymyr-basiuk Jan 12, 2024
898b55d
cleanup resolvers & fix IssuerResolver
volodymyr-basiuk Jan 12, 2024
c6ce8e8
Merge branch 'main' into feature/validate-cred-proof
volodymyr-basiuk Jan 15, 2024
2a96369
resolve comments
volodymyr-basiuk Jan 16, 2024
b5c2484
fix linter
volodymyr-basiuk Jan 16, 2024
3558742
add customHTTPClient option
volodymyr-basiuk Jan 16, 2024
3261365
merge main
volodymyr-basiuk Jan 16, 2024
b397a79
tidy
volodymyr-basiuk Jan 16, 2024
6800894
move cred status opt to resolvers
volodymyr-basiuk Jan 16, 2024
433544d
fix linter
volodymyr-basiuk Jan 16, 2024
e990c8a
comment agent mock
volodymyr-basiuk Jan 16, 2024
63f6389
cleanup
volodymyr-basiuk Jan 16, 2024
14e3a4f
rm iden3comm dep
volodymyr-basiuk Jan 17, 2024
0ed1394
remove httpmock dep
volodymyr-basiuk Jan 17, 2024
24ac312
pass revNonce as param (remove bigIntByPath)
volodymyr-basiuk Jan 18, 2024
19dec47
add DIDResolver interface & HTTP implementation
volodymyr-basiuk Jan 18, 2024
93422f8
add CredentialStatusResolveOptions
volodymyr-basiuk Jan 18, 2024
f1f7487
pass resolveOptions
volodymyr-basiuk Jan 18, 2024
6363011
pvovide CredentialStatusResolveOpt...
volodymyr-basiuk Jan 19, 2024
2bfce5b
pass state in DIDResolver via DID Query
volodymyr-basiuk Jan 22, 2024
a5bd660
rm var
volodymyr-basiuk Jan 22, 2024
c7b98ff
Some nodes and POCs for proof validations
olomix Jan 22, 2024
95d551c
merged with upstream
olomix Jan 22, 2024
faabfa4
Rename error name to be consistent with other
olomix Jan 22, 2024
13758ee
make linters happy
olomix Jan 22, 2024
6de3e4b
temporary disable testing on go < 1.21
olomix Jan 22, 2024
7245bfd
Refactor proof validation
olomix Jan 23, 2024
c5028a2
Upgrade linter version and fix lint errors
olomix Jan 23, 2024
52e3f8d
Fix auth claim revocation status check
olomix Jan 24, 2024
b6f3210
Return setter/getter for issuer DID to go-schema-processor
olomix Jan 26, 2024
894ef55
Merge pull request #88 from iden3/feature/validate-cred-proof-2
volodymyr-basiuk Jan 26, 2024
c3c8022
resolve comments
volodymyr-basiuk Feb 2, 2024
8ded2e3
rewrite resp body err only if proof err nil
volodymyr-basiuk Feb 5, 2024
2ce69e4
limit reader check
volodymyr-basiuk Feb 6, 2024
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
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ require (
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35
github.com/santhosh-tekuri/jsonschema/v5 v5.3.0
github.com/stretchr/testify v1.8.4
golang.org/x/crypto v0.7.0
golang.org/x/crypto v0.12.0
)

require (
vmidyllic marked this conversation as resolved.
Show resolved Hide resolved
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dchest/blake512 v1.0.0 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/sys v0.15.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
16 changes: 11 additions & 5 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ github.com/iden3/go-iden3-crypto v0.0.15 h1:4MJYlrot1l31Fzlo2sF56u7EVFeHHJkxGXXZ
github.com/iden3/go-iden3-crypto v0.0.15/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E=
github.com/iden3/go-merkletree-sql/v2 v2.0.4 h1:Dp089P3YNX1BE8+T1tKQHWTtnk84Y/Kr7ZAGTqwscoY=
github.com/iden3/go-merkletree-sql/v2 v2.0.4/go.mod h1:kRhHKYpui5DUsry5RpveP6IC4XMe6iApdV9VChRYuEk=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
Expand All @@ -23,11 +28,12 @@ github.com/santhosh-tekuri/jsonschema/v5 v5.3.0 h1:uIkTLo0AGRc8l7h5l9r+GcYi9qfVP
github.com/santhosh-tekuri/jsonschema/v5 v5.3.0/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
274 changes: 266 additions & 8 deletions verifiable/credential.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@ package verifiable
import (
"bytes"
"context"
"encoding/hex"
"encoding/json"
"fmt"
"math/big"
"time"

core "github.com/iden3/go-iden3-core/v2"
mt "github.com/iden3/go-merkletree-sql/v2"
"github.com/iden3/go-iden3-core/v2/w3c"
"github.com/iden3/go-iden3-crypto/babyjub"
"github.com/iden3/go-iden3-crypto/poseidon"
"github.com/iden3/go-merkletree-sql/v2"
"github.com/iden3/go-schema-processor/v2/merklize"
"github.com/pkg/errors"
)
Expand All @@ -29,6 +35,238 @@ type W3CCredential struct {
DisplayMethod *DisplayMethod `json:"displayMethod,omitempty"`
}

// VerifyProof verify credential proof
func (vc *W3CCredential) VerifyProof(proofType ProofType, didResolver DIDResolver, opts ...W3CProofVerificationOpt) error {
verifyConfig := W3CProofVerificationConfig{}
for _, o := range opts {
o(&verifyConfig)
}

var (
credProof CredentialProof
coreClaim *core.Claim
)
for _, p := range vc.Proof {
if p.ProofType() == proofType {
credProof = p
volodymyr-basiuk marked this conversation as resolved.
Show resolved Hide resolved
break
}
}
if credProof == nil {
return ErrProofNotFound
}

coreClaim, err := credProof.GetCoreClaim()
if err != nil {
return errors.New("can't get core claim")
}

var credProofBytes []byte
credProofBytes, err = json.Marshal(credProof)
if err != nil {
return err
}
switch proofType {
case BJJSignatureProofType:
var proof BJJSignatureProof2021
err = json.Unmarshal(credProofBytes, &proof)
if err != nil {
return err
}

var userDID *w3c.DID
credSubjID, ok := vc.CredentialSubject["id"]
if ok {
credSubjString := fmt.Sprintf("%v", credSubjID)
userDID, err = w3c.ParseDID(credSubjString)
if err != nil {
return err
}
}
return verifyBJJSignatureProof(proof, coreClaim, didResolver, userDID, verifyConfig)
case Iden3SparseMerkleTreeProofType:
var proof Iden3SparseMerkleTreeProof
err = json.Unmarshal(credProofBytes, &proof)
if err != nil {
return err
}
return verifyIden3SparseMerkleTreeProof(proof, coreClaim, didResolver)
default:
return ErrorProofNotSupported
}
}

func verifyBJJSignatureProof(proof BJJSignatureProof2021, coreClaim *core.Claim, didResolver DIDResolver, userDID *w3c.DID, verifyConfig W3CProofVerificationConfig) error {
// issuer claim
authClaim := &core.Claim{}
err := authClaim.FromHex(proof.IssuerData.AuthCoreClaim)
if err != nil {
return err
}

rawSlotInts := authClaim.RawSlotsAsInts()
var publicKey babyjub.PublicKey
publicKey.X = rawSlotInts[2] // Ax should be in indexSlotA
publicKey.Y = rawSlotInts[3] // Ay should be in indexSlotB

sig, err := bjjSignatureFromHexString(proof.Signature)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can use already exists bjj.SignatureComp. This method has MarshalText and UnmarshalText that encode/decode from/to hex.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MarshalText/UnmarshalText works with bytes, inside bjjSignatureFromHexString we just converting to byres and call Decompress

if err != nil || sig == nil {
return err
}

// core claim hash
hi, hv, err := coreClaim.HiHv()
if err != nil {
return err
}

claimHash, err := poseidon.Hash([]*big.Int{hi, hv})
if err != nil {
return err
}

valid := publicKey.VerifyPoseidon(claimHash, sig)

if !valid {
return err
}

issuerDID, err := w3c.ParseDID(proof.IssuerData.ID)
if err != nil {
return err
}

issuerStateHash, err := merkletree.NewHashFromHex(*proof.IssuerData.State.Value)
if err != nil {
return fmt.Errorf("invalid state formant: %v", err)
}

didDoc, err := didResolver.Resolve(context.Background(), issuerDID, issuerStateHash.BigInt())
volodymyr-basiuk marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return err
}

vm, err := getIden3StateInfo2023FromDIDDocument(didDoc)
if err != nil {
return err
}

// Published or genesis
if !*vm.IdentityState.Published {
var (
isGenesisState bool
issuerID core.ID
)
issuerID, err = core.IDFromDID(*issuerDID)
if err != nil {
return err
}
isGenesisState, err = core.CheckGenesisStateID(issuerID.BigInt(), issuerStateHash.BigInt())
if err != nil {
return err
}
if !isGenesisState {
return errors.New("issuer state not published and not genesis")
}
}

_, err = ValidateCredentialStatus(proof.IssuerData.CredentialStatus, coreClaim.GetRevocationNonce(), verifyConfig.StatusResolverRegistry, issuerDID, userDID)
if err != nil {
return err
}
return nil
}

func verifyIden3SparseMerkleTreeProof(proof Iden3SparseMerkleTreeProof, coreClaim *core.Claim, didResolver DIDResolver) error {
var err error

issuerDID, err := w3c.ParseDID(proof.IssuerData.ID)
if err != nil {
return err
}

issuerStateHash, err := merkletree.NewHashFromHex(*proof.IssuerData.State.Value)
if err != nil {
return fmt.Errorf("invalid state formant: %v", err)
}

didDoc, err := didResolver.Resolve(context.Background(), issuerDID, issuerStateHash.BigInt())
if err != nil {
return err
}

vm, err := getIden3StateInfo2023FromDIDDocument(didDoc)
if err != nil {
return err
}

// Published or genesis
if !*vm.IdentityState.Published {
var (
isGenesisState bool
issuerID core.ID
)
issuerID, err = core.IDFromDID(*issuerDID)
if err != nil {
return err
}
isGenesisState, err = core.CheckGenesisStateID(issuerID.BigInt(), issuerStateHash.BigInt())
if err != nil {
return err
}
if !isGenesisState {
return errors.New("issuer state not published and not genesis")
}
}

// 3. root from proof == issuerData.state.сlaimsTreeRoot
hi, hv, err := coreClaim.HiHv()
if err != nil {
return err
}

rootFromProof, err := merkletree.RootFromProof(proof.MTP, hi, hv)
if err != nil {
return err
}
issuerShateHash, err := merkletree.NewHashFromHex(*proof.IssuerData.State.ClaimsTreeRoot)
volodymyr-basiuk marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return fmt.Errorf("invalid state formant: %v", err)
}

if rootFromProof.BigInt().Cmp(issuerShateHash.BigInt()) != 0 {
return errors.New("verifyIden3SparseMerkleTreeProof: root from proof not equal to issuer data claims tree root")
}

return nil
}

func bjjSignatureFromHexString(sigHex string) (*babyjub.Signature, error) {
signatureBytes, err := hex.DecodeString(sigHex)
if err != nil {
return nil, errors.WithStack(err)
}
var sig [64]byte
copy(sig[:], signatureBytes)
bjjSig, err := new(babyjub.Signature).Decompress(sig)
return bjjSig, errors.WithStack(err)
}

func getIden3StateInfo2023FromDIDDocument(document DIDDocument) (*CommonVerificationMethod, error) {
var iden3StateInfo2023 *CommonVerificationMethod
for _, a := range document.VerificationMethod {
if a.Type == "Iden3StateInfo2023" {
a2 := a
iden3StateInfo2023 = &a2
volodymyr-basiuk marked this conversation as resolved.
Show resolved Hide resolved
}
}
if iden3StateInfo2023 == nil {
return nil, errors.New("Issuer Iden3StateInfo2023 auth info not found")
}

return iden3StateInfo2023, nil
}

// Merklize merklizes verifiable credential
func (vc *W3CCredential) Merklize(ctx context.Context,
opts ...merklize.MerklizeOption) (*merklize.Merklizer, error) {
Expand Down Expand Up @@ -62,6 +300,9 @@ func (vc *W3CCredential) Merklize(ctx context.Context,
// ErrProofNotFound is an error when specific proof is not found in the credential
var ErrProofNotFound = errors.New("proof not found")

// ErrorProofNotSupported is an error when specific proof is not supported for validation
var ErrorProofNotSupported = errors.New("proof not supported")
vmidyllic marked this conversation as resolved.
Show resolved Hide resolved

// GetCoreClaimFromProof returns core claim from given proof
func (vc *W3CCredential) GetCoreClaimFromProof(proofType ProofType) (*core.Claim, error) {
for _, p := range vc.Proof {
Expand Down Expand Up @@ -100,11 +341,28 @@ type CredentialStatusType string

// RevocationStatus status of revocation nonce. Info required to check revocation state of claim in circuits
type RevocationStatus struct {
Issuer struct {
State *string `json:"state,omitempty"`
RootOfRoots *string `json:"rootOfRoots,omitempty"`
ClaimsTreeRoot *string `json:"claimsTreeRoot,omitempty"`
RevocationTreeRoot *string `json:"revocationTreeRoot,omitempty"`
} `json:"issuer"`
MTP mt.Proof `json:"mtp"`
Issuer Issuer `json:"issuer"`
MTP merkletree.Proof `json:"mtp"`
}

type Issuer struct {
vmidyllic marked this conversation as resolved.
Show resolved Hide resolved
State *string `json:"state,omitempty"`
vmidyllic marked this conversation as resolved.
Show resolved Hide resolved
RootOfRoots *string `json:"rootOfRoots,omitempty"`
ClaimsTreeRoot *string `json:"claimsTreeRoot,omitempty"`
RevocationTreeRoot *string `json:"revocationTreeRoot,omitempty"`
}

// WithStatusResolverRegistry return new options
func WithStatusResolverRegistry(registry *CredentialStatusResolverRegistry) W3CProofVerificationOpt {
vmidyllic marked this conversation as resolved.
Show resolved Hide resolved
return func(opts *W3CProofVerificationConfig) {
opts.StatusResolverRegistry = registry
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add option with custom HTTP provider.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added

// W3CProofVerificationOpt returns configuration options for W3C proof verification
type W3CProofVerificationOpt func(opts *W3CProofVerificationConfig)

// W3CProofVerificationConfig options for W3C proof verification
type W3CProofVerificationConfig struct {
StatusResolverRegistry *CredentialStatusResolverRegistry
}
Loading