diff --git a/api/net.go b/api/net.go index 6e87cc75c..d4d78310d 100644 --- a/api/net.go +++ b/api/net.go @@ -17,7 +17,6 @@ limitations under the License. package api import ( - idemix2 "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" "github.com/cloudflare/cfssl/signer" ) @@ -46,8 +45,9 @@ type EnrollmentRequestNet struct { // IdemixEnrollmentRequestNet is a request to enroll an identity and get idemix credential type IdemixEnrollmentRequestNet struct { - *idemix2.CredRequest `json:"request"` - CAName string `json:"caname"` + CredRequest []byte `json:"request"` + IssuerNonce []byte `json:"nonce"` + CAName string `json:"caname"` } // ReenrollmentRequestNet is a request to reenroll an identity. diff --git a/go.mod b/go.mod index 2bd799199..27b86c2fc 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,11 @@ module github.com/hyperledger/fabric-ca -go 1.20 +go 1.19 require ( - github.com/IBM/idemix v0.0.2-0.20230510082947-a0c3ee5ebe35 - github.com/IBM/mathlib v0.0.3-0.20231011094432-44ee0eb539da + github.com/IBM/idemix v0.0.2-0.20230914074304-f8eb4a0f6606 + github.com/IBM/idemix/bccsp/types v0.0.0-20230914074304-f8eb4a0f6606 + github.com/IBM/mathlib v0.0.3-0.20230831091907-c532c4d3b65c github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible github.com/cloudflare/cfssl v1.4.1 github.com/felixge/httpsnoop v1.0.1 @@ -15,7 +16,6 @@ require ( github.com/gorilla/mux v1.8.0 github.com/grantae/certinfo v0.0.0-20170412194111-59d56a35515b github.com/hyperledger/fabric v0.0.0-20240123171006-370ebe56cea8 - github.com/hyperledger/fabric-amcl v0.0.0-20230602173724-9e02669dceb2 github.com/hyperledger/fabric-lib-go v1.0.0 github.com/jinzhu/copier v0.3.5 github.com/jmoiron/sqlx v1.2.0 @@ -32,12 +32,15 @@ require ( github.com/spf13/cobra v1.5.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.7.0 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.8.4 golang.org/x/crypto v0.17.0 gopkg.in/ldap.v2 v2.5.1 ) require ( + github.com/IBM/idemix/bccsp/schemes/aries v0.0.0-20230818093228-308e96f529c9 // indirect + github.com/IBM/idemix/bccsp/schemes/weak-bb v0.0.0-20230818093228-308e96f529c9 // indirect + github.com/ale-linux/aries-framework-go/component/kmscrypto v0.0.0-20230817163708-4b3de6d91874 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.7.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -48,6 +51,7 @@ require ( github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/google/certificate-transparency-go v1.0.21 // indirect github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hyperledger/fabric-amcl v0.0.0-20230602173724-9e02669dceb2 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jmhodges/clock v0.0.0-20160418191101-880ee4c33548 // indirect github.com/kilic/bls12-381 v0.1.0 // indirect diff --git a/go.sum b/go.sum index 086e8d03b..be14930ab 100644 --- a/go.sum +++ b/go.sum @@ -37,10 +37,16 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0= github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0= -github.com/IBM/idemix v0.0.2-0.20230510082947-a0c3ee5ebe35 h1:2ucH1TS+pe18L6R2jww9UX5I8GrVWkwjcDbIChDUyfc= -github.com/IBM/idemix v0.0.2-0.20230510082947-a0c3ee5ebe35/go.mod h1:LKHePSqL9g67JezDTU8Y/HCRyzBP3zYbY7q9Hllx6y0= -github.com/IBM/mathlib v0.0.3-0.20231011094432-44ee0eb539da h1:qqGozq4tF6EOVnWoTgBoJGudRKKZXSAYnEtDggzTnsw= -github.com/IBM/mathlib v0.0.3-0.20231011094432-44ee0eb539da/go.mod h1:Tco9QzE3fQzjMS7nPbHDeFfydAzctStf1Pa8hsh6Hjs= +github.com/IBM/idemix v0.0.2-0.20230914074304-f8eb4a0f6606 h1:2F/8jqTCOyCxwpDaaLFd+eYlCtvl03f9owb/YTD+2v0= +github.com/IBM/idemix v0.0.2-0.20230914074304-f8eb4a0f6606/go.mod h1:A9w6lDhpXujKBck1rcw6unxAzJtx+QsuT5qd1CS7BKc= +github.com/IBM/idemix/bccsp/schemes/aries v0.0.0-20230818093228-308e96f529c9 h1:Epm1kxDcOvZug9zY/McvBPyGd7zOiXvzfwZ9cF5zomY= +github.com/IBM/idemix/bccsp/schemes/aries v0.0.0-20230818093228-308e96f529c9/go.mod h1:V1hC/kU/g+eZ2GOlSj4WBTB7FuGbWI0w4U2z7a4i/ew= +github.com/IBM/idemix/bccsp/schemes/weak-bb v0.0.0-20230818093228-308e96f529c9 h1:Lc0yhsKb1+LcJd8U62cLB6WDVpm7kcQLwJXN17Da5G4= +github.com/IBM/idemix/bccsp/schemes/weak-bb v0.0.0-20230818093228-308e96f529c9/go.mod h1:n86zzYREwJdjUybye3rerxmAFfgVJtPvNOrRX30CbrU= +github.com/IBM/idemix/bccsp/types v0.0.0-20230914074304-f8eb4a0f6606 h1:Fp53nUsttyhgkFc33rOxDSElCVbxIOsovGF3EwiOUvI= +github.com/IBM/idemix/bccsp/types v0.0.0-20230914074304-f8eb4a0f6606/go.mod h1:2lO6nmsoSH3WDJdat4kIicU+WTs4qDntGZkF8XdgHm8= +github.com/IBM/mathlib v0.0.3-0.20230831091907-c532c4d3b65c h1:QVrlfdfx7MslApfSdSLiLIOFmYlLhP6wY4LyRBVed4I= +github.com/IBM/mathlib v0.0.3-0.20230831091907-c532c4d3b65c/go.mod h1:k0NBSWMYVgaZ2keDuI8DSwdIEhUNhp8XnlVmm6Xwyuk= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -50,6 +56,8 @@ github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrd github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c= +github.com/ale-linux/aries-framework-go/component/kmscrypto v0.0.0-20230817163708-4b3de6d91874 h1:O08ZCyb1f7UeyOmTeItAw7eSZOlyM0fBnrPgaYgKEiA= +github.com/ale-linux/aries-framework-go/component/kmscrypto v0.0.0-20230817163708-4b3de6d91874/go.mod h1:4sHtFlGI84SVjaSW7u1pCfC0bjijd9ZeqbKptU/Qljs= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -74,6 +82,7 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -269,6 +278,7 @@ github.com/hyperledger/fabric-amcl v0.0.0-20230602173724-9e02669dceb2 h1:B1Nt8hK github.com/hyperledger/fabric-amcl v0.0.0-20230602173724-9e02669dceb2/go.mod h1:X+DIyUsaTmalOpmpQfIvFZjKHQedrURQ5t4YqquX7lE= github.com/hyperledger/fabric-lib-go v1.0.0 h1:UL1w7c9LvHZUSkIvHTDGklxFv2kTeva1QI2emOVc324= github.com/hyperledger/fabric-lib-go v1.0.0/go.mod h1:H362nMlunurmHwkYqR5uHL2UDWbQdbfz74n8kbCFsqc= +github.com/hyperledger/fabric-protos-go v0.3.2 h1:mQmbHw3lyDV2+W5b0FitV/SjM4LLDjMTQJWW9taitDM= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= @@ -450,7 +460,7 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -500,8 +510,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/sykesm/zap-logfmt v0.0.4 h1:U2WzRvmIWG1wDLCFY3sz8UeEmsdHQjHFNlIdmroVFaI= diff --git a/lib/ca.go b/lib/ca.go index 3e75cb2e5..71f23c955 100644 --- a/lib/ca.go +++ b/lib/ca.go @@ -21,6 +21,10 @@ import ( "sync" "time" + i "github.com/IBM/idemix/bccsp" + "github.com/IBM/idemix/bccsp/keystore" + "github.com/IBM/idemix/bccsp/schemes/dlog/crypto/translator/amcl" + math "github.com/IBM/mathlib" "github.com/cloudflare/cfssl/certdb" "github.com/cloudflare/cfssl/config" cfcsr "github.com/cloudflare/cfssl/csr" @@ -32,7 +36,6 @@ import ( "github.com/hyperledger/fabric-ca/lib/attr" "github.com/hyperledger/fabric-ca/lib/attrmgr" "github.com/hyperledger/fabric-ca/lib/caerrors" - idemix2 "github.com/hyperledger/fabric-ca/lib/common/idemix" "github.com/hyperledger/fabric-ca/lib/metadata" "github.com/hyperledger/fabric-ca/lib/server/db" cadb "github.com/hyperledger/fabric-ca/lib/server/db" @@ -138,8 +141,14 @@ func initCA(ca *CA, homeDir string, config *CAConfig, server *Server, renew bool if err != nil { return err } + + CSP, err := i.New(&keystore.Dummy{}, curveID, &amcl.Gurvy{C: curveID}, true) + if err != nil { + return err + } + ca.issuer = idemix.NewIssuer(ca.Config.CA.Name, ca.HomeDir, - &ca.Config.Idemix, ca.csp, idemix.NewLib(curveID), curveID) + &ca.Config.Idemix, CSP) err = ca.issuer.Init(renew, ca.db, ca.levels) if err != nil { return errors.WithMessage(err, fmt.Sprintf("Failed to initialize Idemix issuer for CA '%s'", err.Error())) @@ -1296,17 +1305,18 @@ func getMigrator(driverName string, tx cadb.FabricCATx, curLevels, srvLevels *db return migrator, nil } -func curveIDFromConfig(idemixCurveName string) (idemix2.CurveID, error) { - if idemixCurveName == "" { - idemixCurveName = idemix2.DefaultIdemixCurve - log.Debugf("CurveID for Idemix not specified, defaulting to %s", idemixCurveName) - return idemix2.Curves.ByName(idemixCurveName), nil - } +func curveIDFromConfig(idemixCurveName string) (*math.Curve, error) { + return math.Curves[math.BLS12_381_BBS], nil - curveID := idemix2.Curves.ByName(idemixCurveName) - if curveID == idemix2.Undefined { - return 0, errors.Errorf("CurveID '%s' doesn't exist, expecting one of %s", idemixCurveName, idemix2.Curves.Names()) - } - log.Debugf("Using curve %s for Idemix", idemixCurveName) - return curveID, nil + // if idemixCurveName == "" { + // log.Debugf("CurveID for Idemix not specified, defaulting to %s", idemixCurveName) + // return math.Curves[math.BLS12_381_BBS], nil + // } + + // curveID := idemix2.Curves.ByName(idemixCurveName) + // if curveID == idemix2.Undefined { + // return 0, errors.Errorf("CurveID '%s' doesn't exist, expecting one of %s", idemixCurveName, idemix2.Curves.Names()) + // } + // log.Debugf("Using curve %s for Idemix", idemixCurveName) + // return curveID, nil } diff --git a/lib/client.go b/lib/client.go index 057fb0de0..5ae38a8db 100644 --- a/lib/client.go +++ b/lib/client.go @@ -12,17 +12,28 @@ import ( "encoding/hex" "encoding/json" "fmt" - idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" - math "github.com/IBM/mathlib" + "io" + "net" + "net/http" + "net/url" + "os" + "path" + "path/filepath" + "strconv" + "strings" + + i "github.com/IBM/idemix/bccsp" + "github.com/IBM/idemix/bccsp/keystore" + "github.com/IBM/idemix/bccsp/schemes/dlog/crypto/translator/amcl" + "github.com/IBM/idemix/bccsp/types" + ibccsp "github.com/IBM/idemix/bccsp/types" cfsslapi "github.com/cloudflare/cfssl/api" "github.com/cloudflare/cfssl/csr" "github.com/cloudflare/cfssl/log" - proto "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric-ca/api" "github.com/hyperledger/fabric-ca/lib/client/credential" idemixcred "github.com/hyperledger/fabric-ca/lib/client/credential/idemix" x509cred "github.com/hyperledger/fabric-ca/lib/client/credential/x509" - cidemix "github.com/hyperledger/fabric-ca/lib/common/idemix" sidemix "github.com/hyperledger/fabric-ca/lib/server/idemix" "github.com/hyperledger/fabric-ca/lib/streamer" "github.com/hyperledger/fabric-ca/lib/tls" @@ -31,15 +42,6 @@ import ( cspsigner "github.com/hyperledger/fabric/bccsp/signer" "github.com/mitchellh/mapstructure" "github.com/pkg/errors" - "io" - "net" - "net/http" - "net/url" - "os" - "path" - "path/filepath" - "strconv" - "strings" ) // Client is the fabric-ca client object @@ -57,10 +59,8 @@ type Client struct { // HTTP client associated with this Fabric CA client httpClient *http.Client // Public key of Idemix issuer - issuerPublicKey *idemix.IssuerPublicKey - idemix *idemix.Idemix - curve *math.Curve - curveID cidemix.CurveID + issuerPublicKey ibccsp.Key + iCSP ibccsp.BCCSP } // GetCAInfoResponse is the response from the GetCAInfo call @@ -142,13 +142,17 @@ func (c *Client) Init() error { return err } - curveID, err := curveIDFromConfig(cfg.Idemix.Curve) + curve, err := curveIDFromConfig(cfg.Idemix.Curve) + if err != nil { + return err + } + + CSP, err := i.New(&keystore.Dummy{}, curve, &amcl.Gurvy{C: curve}, true) if err != nil { return err } - c.curveID = curveID - c.curve = cidemix.CurveByID(curveID) - c.idemix = cidemix.InstanceForCurve(curveID) + + c.iCSP = CSP // Successfully initialized the client c.initialized = true @@ -401,7 +405,7 @@ func (c *Client) handleIdemixEnroll(req *api.EnrollmentRequest) (*EnrollmentResp fmt.Sprintf("Failed to decode nonce that was returned by CA %s", req.CAName)) } - nonce := c.curve.NewZrFromBytes(nonceBytes) + nonce := nonceBytes log.Infof("Successfully got nonce from CA %s", req.CAName) ipkBytes, err := util.B64Decode(result.CAInfo.IssuerPublicKey) @@ -414,6 +418,7 @@ func (c *Client) handleIdemixEnroll(req *api.EnrollmentRequest) (*EnrollmentResp return nil, errors.WithMessage(err, "Failed to create an Idemix credential request") } reqNet.CredRequest = credReq + reqNet.IssuerNonce = nonceBytes log.Info("Successfully created an Idemix credential request") body, err = util.Marshal(reqNet, "CredentialRequest") @@ -499,7 +504,7 @@ func (c *Client) newEnrollmentResponse(result *api.EnrollmentResponseNet, id str // newIdemixEnrollmentResponse creates a client idemix enrollment response from a network response func (c *Client) newIdemixEnrollmentResponse(identity *Identity, result *api.IdemixEnrollmentResponseNet, - sk *math.Zr, id string) (*EnrollmentResponse, error) { + usk ibccsp.Key, id string) (*EnrollmentResponse, error) { log.Debugf("newIdemixEnrollmentResponse %s", id) credBytes, err := util.B64Decode(result.Credential) if err != nil { @@ -511,6 +516,11 @@ func (c *Client) newIdemixEnrollmentResponse(identity *Identity, result *api.Ide return nil, errors.WithMessage(err, "Invalid response format from server") } + uskBytes, err := usk.Bytes() + if err != nil { + return nil, errors.WithMessage(err, "converting usk to byte failed") + } + // Create SignerConfig object with credential bytes from the response // and secret key role, _ := result.Attrs["Role"].(int) @@ -518,9 +528,8 @@ func (c *Client) newIdemixEnrollmentResponse(identity *Identity, result *api.Ide enrollmentID, _ := result.Attrs["EnrollmentID"].(string) revocationHandle := result.Attrs[sidemix.AttrRevocationHandle].(string) signerConfig := &idemixcred.SignerConfig{ - CurveID: cidemix.Curves.ByID(c.curveID), Cred: credBytes, - Sk: sk.Bytes(), + USk: uskBytes, Role: role, OrganizationalUnitIdentifier: ou, EnrollmentID: enrollmentID, @@ -529,7 +538,7 @@ func (c *Client) newIdemixEnrollmentResponse(identity *Identity, result *api.Ide } // Create IdemixCredential object - cred := idemixcred.NewCredential(c.idemixCredFile, c, c.curveID) + cred := idemixcred.NewCredential(c.idemixCredFile, c, c.iCSP) err = cred.SetVal(signerConfig) if err != nil { return nil, err @@ -584,22 +593,46 @@ func (c *Client) newCertificateRequest(req *api.CSRInfo, id string) *csr.Certifi // newIdemixCredentialRequest returns CredentialRequest object, a secret key, and a random number used in // the creation of credential request. -func (c *Client) newIdemixCredentialRequest(nonce *math.Zr, ipkBytes []byte) (*idemix.CredRequest, *math.Zr, error) { - rand, err := c.curve.Rand() +func (c *Client) newIdemixCredentialRequest(nonce []byte, ipkBytes []byte) ([]byte, ibccsp.Key, error) { + issuerPubKey, err := c.getIssuerPubKey(ipkBytes) if err != nil { - return nil, nil, errors.Errorf("failed obtaining randomness source: %v", err) + return nil, nil, err } - sk := c.curve.NewRandomZr(rand) - issuerPubKey, err := c.getIssuerPubKey(ipkBytes) + UserKey, err := c.iCSP.KeyGen(&ibccsp.IdemixUserSecretKeyGenOpts{Temporary: true}) + if err != nil { + return nil, nil, err + } + + credRequest, err := c.iCSP.Sign( + UserKey, + nil, + &ibccsp.IdemixCredentialRequestSignerOpts{IssuerPK: issuerPubKey, IssuerNonce: nonce}, + ) if err != nil { return nil, nil, err } - credReq, err := c.idemix.NewCredRequest(sk, nonce.Bytes(), issuerPubKey, rand, c.idemix.Translator) - return credReq, sk, err + + return credRequest, UserKey, err +} + +const ( + // AttrEnrollmentID is the attribute name for enrollment ID + AttrEnrollmentID = "EnrollmentID" + // AttrRole is the attribute name for role + AttrRole = "Role" + // AttrOU is the attribute name for OU + AttrOU = "OU" + // AttrRevocationHandle is the attribute name for revocation handle + AttrRevocationHandle = "RevocationHandle" +) + +// GetAttributeNames returns attribute names supported by the Fabric CA for Idemix credentials +func GetAttributeNames() []string { + return []string{AttrOU, AttrRole, AttrEnrollmentID, AttrRevocationHandle} } -func (c *Client) getIssuerPubKey(ipkBytes []byte) (*idemix.IssuerPublicKey, error) { +func (c *Client) getIssuerPubKey(ipkBytes []byte) (ibccsp.Key, error) { var err error if len(ipkBytes) == 0 { ipkBytes, err = os.ReadFile(c.ipkFile) @@ -607,13 +640,13 @@ func (c *Client) getIssuerPubKey(ipkBytes []byte) (*idemix.IssuerPublicKey, erro return nil, errors.Wrapf(err, "Error reading CA's Idemix public key at '%s'", c.ipkFile) } } - pubKey := &idemix.IssuerPublicKey{} - err = proto.Unmarshal(ipkBytes, pubKey) + + ipk, err := c.iCSP.KeyImport(ipkBytes, &ibccsp.IdemixIssuerPublicKeyImportOpts{AttributeNames: GetAttributeNames(), Temporary: true}) if err != nil { - return nil, err + return nil, errors.Wrapf(err, "Error importing issuer public key") } - c.issuerPublicKey = pubKey - return c.issuerPublicKey, nil + + return ipk, nil } // LoadMyIdentity loads the client's identity from disk @@ -645,7 +678,7 @@ func (c *Client) LoadIdentity(keyFile, certFile, idemixCredFile string) (*Identi log.Debugf("No X509 credential found at %s, %s", keyFile, certFile) } - idemixCred := idemixcred.NewCredential(idemixCredFile, c, c.curveID) + idemixCred := idemixcred.NewCredential(idemixCredFile, c, c.iCSP) err = idemixCred.Load() if err == nil { idemixFound = true @@ -718,7 +751,7 @@ func (c *Client) GetCSP() bccsp.BCCSP { } // GetIssuerPubKey returns issuer public key associated with this client -func (c *Client) GetIssuerPubKey() (*idemix.IssuerPublicKey, error) { +func (c *Client) GetIssuerPubKey() (ibccsp.Key, error) { if c.issuerPublicKey == nil { return c.getIssuerPubKey(nil) } @@ -951,18 +984,30 @@ func (c *Client) verifyIdemixCredential() error { return errors.Wrapf(err, "Failed to unmarshal signer config from %s", c.idemixCredFile) } - cred := new(idemix.Credential) - err = proto.Unmarshal(signerConfig.GetCred(), cred) + skbytes := signerConfig.GetSk() + sk, err := c.iCSP.KeyImport(skbytes, &types.IdemixUserSecretKeyGenOpts{}) if err != nil { - return errors.Wrap(err, "Failed to unmarshal Idemix credential from signer config") + return errors.Wrapf(err, "Failed to import users private key") } - sk := c.curve.NewZrFromBytes(signerConfig.GetSk()) - // Verify that the credential is cryptographically valid - err = cred.Ver(sk, ipk, c.curve, c.idemix.Translator) - if err != nil { + valid, err := c.iCSP.Verify( + sk, + signerConfig.GetCred(), + nil, + &types.IdemixCredentialSignerOpts{ + IssuerPK: ipk, + Attributes: []types.IdemixAttribute{ + {Type: types.IdemixBytesAttribute, Value: []byte{0}}, + {Type: types.IdemixBytesAttribute, Value: []byte{0, 1}}, + {Type: types.IdemixIntAttribute, Value: 1}, + {Type: types.IdemixBytesAttribute, Value: []byte{0, 1, 2}}, + }, + }, + ) + if err != nil || !valid { return errors.Wrap(err, "Idemix credential is not cryptographically valid") } + return nil } diff --git a/lib/client/credential/idemix/credential.go b/lib/client/credential/idemix/credential.go index 3bb004c41..1a9abf3af 100644 --- a/lib/client/credential/idemix/credential.go +++ b/lib/client/credential/idemix/credential.go @@ -11,13 +11,9 @@ import ( "fmt" "net/http" - schemes "github.com/IBM/idemix/bccsp/schemes" - scheme "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" - math "github.com/IBM/mathlib" + "github.com/IBM/idemix/bccsp/types" "github.com/cloudflare/cfssl/log" - "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric-ca/api" - idemix4 "github.com/hyperledger/fabric-ca/lib/common/idemix" "github.com/hyperledger/fabric-ca/util" "github.com/hyperledger/fabric/bccsp" "github.com/pkg/errors" @@ -30,7 +26,7 @@ const ( // Client represents a client that will load/store an Idemix credential type Client interface { - GetIssuerPubKey() (*scheme.IssuerPublicKey, error) + GetIssuerPubKey() (types.Key, error) GetCSP() bccsp.BCCSP } @@ -39,14 +35,13 @@ type Credential struct { client Client signerConfigFile string val *SignerConfig - curve *math.Curve - idemix *scheme.Idemix + csp types.BCCSP } // NewCredential is constructor for idemix.Credential -func NewCredential(signerConfigFile string, c Client, id idemix4.CurveID) *Credential { +func NewCredential(signerConfigFile string, c Client, csp types.BCCSP) *Credential { return &Credential{ - c, signerConfigFile, nil, idemix4.CurveByID(id), idemix4.InstanceForCurve(id), + c, signerConfigFile, nil, csp, } } @@ -121,9 +116,7 @@ func (cred *Credential) Load() error { if err != nil { return errors.Wrapf(err, fmt.Sprintf("Failed to unmarshal SignerConfig bytes from %s", cred.signerConfigFile)) } - if val.CurveID == "" { - val.CurveID = idemix4.DefaultIdemixCurve - } + cred.val = &val return nil } @@ -136,7 +129,10 @@ func (cred *Credential) CreateToken(req *http.Request, reqBody []byte) (string, } // Get user's secret key - sk := cred.curve.NewZrFromBytes(cred.val.GetSk()) + sk, err := cred.csp.KeyImport(cred.val.GetSk(), &types.IdemixUserSecretKeyImportOpts{}) + if err != nil { + return "", errors.WithMessage(err, "Failed to get user secret key") + } // Get issuer public key ipk, err := cred.client.GetIssuerPubKey() @@ -146,12 +142,7 @@ func (cred *Credential) CreateToken(req *http.Request, reqBody []byte) (string, // Generate a fresh Pseudonym (and a corresponding randomness) - rand, err := cred.curve.Rand() - if err != nil { - return "", errors.WithMessage(err, "Failed to get randomness source") - } - - nym, randNym, err := cred.idemix.MakeNym(sk, ipk, rand, cred.idemix.Translator) + NymKey, err := cred.csp.KeyDeriv(sk, &types.IdemixNymKeyDerivationOpts{Temporary: true, IssuerPK: ipk}) if err != nil { return "", errors.WithMessage(err, "failed making Nym") } @@ -165,29 +156,33 @@ func (cred *Credential) CreateToken(req *http.Request, reqBody []byte) (string, return "", errors.WithMessage(digestError, fmt.Sprintf("Failed to create token '%s'", msg)) } - // A disclosure vector is formed (indicating that only enrollment ID from the credential is revealed) - disclosure := []byte{0, 0, 1, 0} - - credential := scheme.Credential{} - err = proto.Unmarshal(cred.val.GetCred(), &credential) - if err != nil { - return "", errors.Wrapf(err, "Failed to unmarshal Idemix credential while creating token") - } - cri := scheme.CredentialRevocationInformation{} - err = proto.Unmarshal(cred.val.GetCredentialRevocationInformation(), &cri) - if err != nil { - return "", errors.Wrapf(err, "Failed to unmarshal Idemix CRI while creating token") - } - - sig, _, err := cred.idemix.NewSignature(&credential, sk, nym, randNym, ipk, disclosure, digest, 3, 0, &cri, rand, cred.idemix.Translator, schemes.Standard, nil) + signOpts := &types.IdemixSignerOpts{ + Credential: cred.val.GetCred(), + Nym: NymKey, + IssuerPK: ipk, + Attributes: []types.IdemixAttribute{ + {Type: types.IdemixHiddenAttribute}, + {Type: types.IdemixHiddenAttribute}, + {Type: types.IdemixHiddenAttribute}, + {Type: types.IdemixHiddenAttribute}, + }, + RhIndex: 3, + EidIndex: 2, + Epoch: 0, + SigType: types.Standard, + CRI: cred.val.GetCredentialRevocationInformation(), + } + + sig, err := cred.csp.Sign( + sk, + digest, + signOpts, + ) if err != nil { return "", errors.Wrapf(err, "Failed to create signature while creating token") } - sigBytes, err := proto.Marshal(sig) - if err != nil { - return "", err - } - token := "idemix." + api.IdemixTokenVersion1 + "." + enrollmentID + "." + util.B64Encode(sigBytes) + + token := "idemix." + api.IdemixTokenVersion1 + "." + enrollmentID + "." + util.B64Encode(sig) return token, nil } diff --git a/lib/client/credential/idemix/credential_test.go b/lib/client/credential/idemix/credential_test.go index 409ce5a42..ab4680bb5 100644 --- a/lib/client/credential/idemix/credential_test.go +++ b/lib/client/credential/idemix/credential_test.go @@ -9,32 +9,24 @@ package idemix_test import ( "bytes" "encoding/json" - "fmt" "net/http" "os" "path/filepath" "strings" "testing" - scheme "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" + i "github.com/IBM/idemix/bccsp" + "github.com/IBM/idemix/bccsp/schemes/dlog/crypto/translator/amcl" + bccsp "github.com/IBM/idemix/bccsp/types" math "github.com/IBM/mathlib" - "github.com/golang/protobuf/proto" lib "github.com/hyperledger/fabric-ca/lib" . "github.com/hyperledger/fabric-ca/lib/client/credential/idemix" - cidemix "github.com/hyperledger/fabric-ca/lib/common/idemix" "github.com/hyperledger/fabric-ca/lib/server/idemix" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) func TestIdemixCredential(t *testing.T) { - for _, curveID := range []cidemix.CurveID{cidemix.Gurvy} { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curveID), func(t *testing.T) { - testIdemixCredential(t, curveID) - }) - } -} - -func testIdemixCredential(t *testing.T, curveID cidemix.CurveID) { testDataDir, err := os.MkdirTemp("", strings.Replace(t.Name(), "/", "-", -1)) assert.NoError(t, err) defer os.RemoveAll(testDataDir) @@ -42,7 +34,7 @@ func testIdemixCredential(t *testing.T, curveID cidemix.CurveID) { testSignerConfigFile := testDataDir + "/IdemixSignerConfig" testIssuerPublicFile := testDataDir + "/IdemixPublicKey" - signerConf, ipk := makeSignerConfigAndIPK(curveID, t) + signerConf, ipk, CSP := makeSignerConfigAndIPK(math.BLS12_381_BBS, t) rawSignerConf, err := json.Marshal(signerConf) if err != nil { t.Fatalf("Failed to marshal signer config: %s", err.Error()) @@ -53,7 +45,7 @@ func testIdemixCredential(t *testing.T, curveID cidemix.CurveID) { t.Fatalf("Failed to write signer config to file: %s", err.Error()) } - rawIPK, err := proto.Marshal(ipk) + rawIPK, err := ipk.Bytes() if err != nil { t.Fatalf("Failed to marshal IPK: %s", err.Error()) } @@ -77,7 +69,7 @@ func testIdemixCredential(t *testing.T, curveID cidemix.CurveID) { t.Fatalf("Failed to initialize client: %s", err.Error()) } - idemixCred := NewCredential(signerConfig, client, curveID) + idemixCred := NewCredential(signerConfig, client, CSP) assert.Equal(t, idemixCred.Type(), CredType, "Type for a IdemixCredential instance must be Idemix") _, err = idemixCred.Val() @@ -204,50 +196,106 @@ func testIdemixCredential(t *testing.T, curveID cidemix.CurveID) { assert.Error(t, err, "RevokeSelf should fail as it is not implemented for Idemix credential") } -func makeSignerConfigAndIPK(curveID cidemix.CurveID, t *testing.T) (SignerConfig, *scheme.IssuerPublicKey) { - curve := cidemix.CurveByID(curveID) - rand, err := curve.Rand() - assert.NoError(t, err) +// NewDummyKeyStore instantiate a dummy key store +// that neither loads nor stores keys +func NewDummyKeyStore() bccsp.KeyStore { + return &dummyKeyStore{} +} + +// dummyKeyStore is a read-only KeyStore that neither loads nor stores keys. +type dummyKeyStore struct { +} + +// ReadOnly returns true if this KeyStore is read only, false otherwise. +// If ReadOnly is true then StoreKey will fail. +func (ks *dummyKeyStore) ReadOnly() bool { + return true +} +// GetKey returns a key object whose SKI is the one passed. +func (ks *dummyKeyStore) GetKey(ski []byte) (bccsp.Key, error) { + return nil, errors.New("Key not found. This is a dummy KeyStore") +} + +// StoreKey stores the key k in this KeyStore. +// If this KeyStore is read only then the method will fail. +func (ks *dummyKeyStore) StoreKey(k bccsp.Key) error { + return nil +} + +func makeSignerConfigAndIPK(curveID math.CurveID, t *testing.T) (SignerConfig, bccsp.Key, bccsp.BCCSP) { attrs := []string{idemix.AttrOU, idemix.AttrRole, idemix.AttrEnrollmentID, idemix.AttrRevocationHandle} - var numericalAttrs []*math.Zr - for _, attr := range attrs { - numericalAttrs = append(numericalAttrs, curve.HashToZr([]byte(attr))) - } - idemix := cidemix.InstanceForCurve(curveID) - ik, err := idemix.NewIssuerKey(attrs, rand, idemix.Translator) + curve := math.Curves[curveID] + CSP, err := i.New(NewDummyKeyStore(), curve, &amcl.Gurvy{C: curve}, true) assert.NoError(t, err) - sk := curve.NewZrFromBytes(ik.Isk) + isk, err := CSP.KeyGen(&bccsp.IdemixIssuerKeyGenOpts{Temporary: true, AttributeNames: attrs}) + assert.NoError(t, err) - revKey, err := idemix.GenerateLongTermRevocationKey() + ipk, err := isk.PublicKey() assert.NoError(t, err) - cri, err := idemix.CreateCRI(revKey, nil, 1, scheme.ALG_NO_REVOCATION, rand, idemix.Translator) + revKey, err := CSP.KeyGen(&bccsp.IdemixRevocationKeyGenOpts{Temporary: true}) assert.NoError(t, err) - criBytes, err := proto.Marshal(cri) + cri, err := CSP.Sign( + revKey, + nil, + &bccsp.IdemixCRISignerOpts{ + UnrevokedHandles: nil, + Epoch: 1, + RevocationAlgorithm: bccsp.AlgNoRevocation, + }, + ) assert.NoError(t, err) - nonce := curve.NewRandomZr(rand) + nonce := []byte("do not reuse me do not reuse me ") - credReq, err := idemix.NewCredRequest(sk, nonce.Bytes(), ik.Ipk, rand, idemix.Translator) + UserKey, err := CSP.KeyGen(&bccsp.IdemixUserSecretKeyGenOpts{Temporary: true}) assert.NoError(t, err) - cred, err := idemix.NewCredential(ik, credReq, numericalAttrs, rand, idemix.Translator) + uskBytes, err := UserKey.Bytes() assert.NoError(t, err) - credBytes, err := proto.Marshal(cred) + credReq, err := CSP.Sign( + UserKey, + nil, + &bccsp.IdemixCredentialRequestSignerOpts{IssuerPK: ipk, IssuerNonce: nonce}, + ) assert.NoError(t, err) - signerSK := curve.NewRandomZr(rand) + cred, err := CSP.Sign( + isk, + credReq, + &bccsp.IdemixCredentialSignerOpts{ + Attributes: []bccsp.IdemixAttribute{ + { + Type: bccsp.IdemixBytesAttribute, + Value: []byte(attrs[0]), + }, + { + Type: bccsp.IdemixIntAttribute, + Value: 1, + }, + { + Type: bccsp.IdemixBytesAttribute, + Value: []byte(attrs[2]), + }, + { + Type: bccsp.IdemixBytesAttribute, + Value: []byte(attrs[3]), + }, + }, + }, + ) + assert.NoError(t, err) return SignerConfig{ - CredentialRevocationInformation: criBytes, - Cred: credBytes, + CredentialRevocationInformation: cri, + Cred: cred, EnrollmentID: "admin", OrganizationalUnitIdentifier: "MSPID", - Sk: signerSK.Bytes(), - }, ik.Ipk + USk: uskBytes, + }, ipk, CSP } diff --git a/lib/client/credential/idemix/signerconfig.go b/lib/client/credential/idemix/signerconfig.go index 51dabcf06..ba747ca60 100644 --- a/lib/client/credential/idemix/signerconfig.go +++ b/lib/client/credential/idemix/signerconfig.go @@ -11,7 +11,7 @@ type SignerConfig struct { // Cred represents the serialized idemix credential of the default signer Cred []byte `protobuf:"bytes,1,opt,name=Cred,proto3" json:"Cred,omitempty"` // Sk is the secret key of the default signer, corresponding to credential Cred - Sk []byte `protobuf:"bytes,2,opt,name=Sk,proto3" json:"Sk,omitempty"` + USk []byte `protobuf:"bytes,2,opt,name=Sk,proto3" json:"Sk,omitempty"` // OrganizationalUnitIdentifier defines the organizational unit the default signer is in OrganizationalUnitIdentifier string `protobuf:"bytes,3,opt,name=organizational_unit_identifier,json=organizationalUnitIdentifier" json:"organizational_unit_identifier,omitempty"` // Role defines whether the default signer is admin, member, peer, or client @@ -20,8 +20,6 @@ type SignerConfig struct { EnrollmentID string `protobuf:"bytes,5,opt,name=enrollment_id,json=enrollmentId" json:"enrollment_id,omitempty"` // CRI contains a serialized Credential Revocation Information CredentialRevocationInformation []byte `protobuf:"bytes,6,opt,name=credential_revocation_information,json=credentialRevocationInformation,proto3" json:"credential_revocation_information,omitempty"` - // CurveID specifies the name of the Idemix curve to use, defaults to 'amcl.Fp256bn' - CurveID string `protobuf:"bytes,7,opt,name=curve_id,json=curveID" json:"curveID,omitempty"` // RevocationHandle is the handle used to single out this credential and determine its revocation status RevocationHandle string `protobuf:"bytes,8,opt,name=revocation_handle,json=revocationHandle,proto3" json:"revocation_handle,omitempty"` } @@ -33,7 +31,7 @@ func (s *SignerConfig) GetCred() []byte { // GetSk returns secret key associated with this signer config func (s *SignerConfig) GetSk() []byte { - return s.Sk + return s.USk } // GetOrganizationalUnitIdentifier returns OU of the user associated with this signer config diff --git a/lib/common/idemix/common.go b/lib/common/idemix/common.go index c84cbc667..3b37abbf3 100644 --- a/lib/common/idemix/common.go +++ b/lib/common/idemix/common.go @@ -6,99 +6,99 @@ SPDX-License-Identifier: Apache-2.0 package idemix -import ( - "fmt" - - idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" - "github.com/IBM/idemix/bccsp/schemes/dlog/crypto/translator/amcl" - math "github.com/IBM/mathlib" -) - -// CurveID defines the index of the possible idemix curves -type CurveID uint8 - -const ( - // Undefined is used to indicate that no curve has been defined - Undefined CurveID = iota - // FP256BN is an AMCL implementation of BN256 and its corresponding finite field - FP256BN - // Gurvy is an implementation of gnark-crypto of BN254 and its corresponding finite field - Gurvy - // FP256BNMiracl is another AMCL implementation of BN256 and its corresponding finite field - FP256BNMiracl -) - -const ( - // DefaultIdemixCurve is the curve picked by Fabric-CA at default - DefaultIdemixCurve = "amcl.Fp256bn" -) - -// CurveIDs defines a collection of CurveIDs -type CurveIDs []CurveID - -var ( - translators = [...]idemix.Translator{&amcl.Fp256bn{C: math.Curves[0]}, &amcl.Gurvy{C: math.Curves[1]}, &amcl.Fp256bnMiracl{C: math.Curves[2]}} - // Curves lists all idemix curves that can be picked by Fabric-CA - Curves = CurveIDs{FP256BN, Gurvy, FP256BNMiracl} - - // curvesByName maps the names of the curves as they appear in the configuration to their CurveID enum. - curvesByName = map[string]CurveID{ - DefaultIdemixCurve: FP256BN, - "gurvy.Bn254": Gurvy, - "amcl.Fp256Miraclbn": FP256BNMiracl, - } -) - -// ByName returns CurveID with the corresponding name, or Undefined -func (cids CurveIDs) ByName(name string) CurveID { - return curvesByName[name] -} - -// ByID returns the name of the given CurveID or an empty string -func (cids CurveIDs) ByID(curveID CurveID) string { - for name, currentCurveID := range curvesByName { - if currentCurveID == curveID { - return name - } - } - return "" -} - -// Names returns the names of the curves according to their order -func (cids CurveIDs) Names() []string { - var res []string - for _, curveID := range curvesByName { - res = append(res, cids.ByID(curveID)) - } - return res -} - -// InstanceForCurve returns an Idemix instance that uses the given CurveID -func InstanceForCurve(id CurveID) *idemix.Idemix { - if id == Undefined { - panic("undefined curve") - } - id-- - if int(id) >= len(math.Curves) { - panic(fmt.Sprintf("CurveID must be in [0,%d]", len(math.Curves)-1)) - } - - idemix := &idemix.Idemix{ - Curve: math.Curves[int(id)], - Translator: translators[int(id)], - } - - return idemix -} - -// CurveByID returns the Mathlib curve that corresponds to the given CurveID -func CurveByID(id CurveID) *math.Curve { - if id == Undefined { - panic("undefined curve") - } - id-- - if int(id) >= len(math.Curves) { - panic(fmt.Sprintf("CurveID must be in [0,%d]", len(math.Curves)-1)) - } - return math.Curves[int(id)] -} +// import ( +// "fmt" + +// idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" +// "github.com/IBM/idemix/bccsp/schemes/dlog/crypto/translator/amcl" +// math "github.com/IBM/mathlib" +// ) + +// // CurveID defines the index of the possible idemix curves +// type CurveID uint8 + +// const ( +// // Undefined is used to indicate that no curve has been defined +// Undefined CurveID = iota +// // FP256BN is an AMCL implementation of BN256 and its corresponding finite field +// FP256BN +// // Gurvy is an implementation of gnark-crypto of BN254 and its corresponding finite field +// Gurvy +// // FP256BNMiracl is another AMCL implementation of BN256 and its corresponding finite field +// FP256BNMiracl +// ) + +// const ( +// // DefaultIdemixCurve is the curve picked by Fabric-CA at default +// DefaultIdemixCurve = "amcl.Fp256bn" +// ) + +// // CurveIDs defines a collection of CurveIDs +// type CurveIDs []CurveID + +// var ( +// translators = [...]idemix.Translator{&amcl.Fp256bn{C: math.Curves[0]}, &amcl.Gurvy{C: math.Curves[1]}, &amcl.Fp256bnMiracl{C: math.Curves[2]}} +// // Curves lists all idemix curves that can be picked by Fabric-CA +// Curves = CurveIDs{FP256BN, Gurvy, FP256BNMiracl} + +// // curvesByName maps the names of the curves as they appear in the configuration to their CurveID enum. +// curvesByName = map[string]CurveID{ +// DefaultIdemixCurve: FP256BN, +// "gurvy.Bn254": Gurvy, +// "amcl.Fp256Miraclbn": FP256BNMiracl, +// } +// ) + +// // ByName returns CurveID with the corresponding name, or Undefined +// func (cids CurveIDs) ByName(name string) CurveID { +// return curvesByName[name] +// } + +// // ByID returns the name of the given CurveID or an empty string +// func (cids CurveIDs) ByID(curveID CurveID) string { +// for name, currentCurveID := range curvesByName { +// if currentCurveID == curveID { +// return name +// } +// } +// return "" +// } + +// // Names returns the names of the curves according to their order +// func (cids CurveIDs) Names() []string { +// var res []string +// for _, curveID := range curvesByName { +// res = append(res, cids.ByID(curveID)) +// } +// return res +// } + +// // InstanceForCurve returns an Idemix instance that uses the given CurveID +// func InstanceForCurve(id CurveID) *idemix.Idemix { +// if id == Undefined { +// panic("undefined curve") +// } +// id-- +// if int(id) >= len(math.Curves) { +// panic(fmt.Sprintf("CurveID must be in [0,%d]", len(math.Curves)-1)) +// } + +// idemix := &idemix.Idemix{ +// Curve: math.Curves[int(id)], +// Translator: translators[int(id)], +// } + +// return idemix +// } + +// // CurveByID returns the Mathlib curve that corresponds to the given CurveID +// func CurveByID(id CurveID) *math.Curve { +// if id == Undefined { +// panic("undefined curve") +// } +// id-- +// if int(id) >= len(math.Curves) { +// panic(fmt.Sprintf("CurveID must be in [0,%d]", len(math.Curves)-1)) +// } +// return math.Curves[int(id)] +// } diff --git a/lib/server/idemix/creddbaccessor.go b/lib/server/idemix/creddbaccessor.go index bdab4a648..49105fa05 100644 --- a/lib/server/idemix/creddbaccessor.go +++ b/lib/server/idemix/creddbaccessor.go @@ -7,6 +7,7 @@ SPDX-License-Identifier: Apache-2.0 package idemix import ( + "database/sql" "fmt" "reflect" "time" @@ -87,6 +88,21 @@ type CredentialAccessor struct { db db.FabricCADB } +//go:generate mockery --name DbFabricCADB --case underscore +type DbFabricCADB interface { + db.FabricCADB +} + +//go:generate mockery --name SqlResult --case underscore +type SqlResult interface { + sql.Result +} + +//go:generate mockery --name DbFabricCATx --case underscore +type DbFabricCATx interface { + db.FabricCATx +} + // NewCredentialAccessor returns a new CredentialAccessor. func NewCredentialAccessor(db db.FabricCADB, level int) CredDBAccessor { ac := new(CredentialAccessor) diff --git a/lib/server/idemix/creddbaccessor_test.go b/lib/server/idemix/creddbaccessor_test.go index 763b44a1f..412f91ad0 100644 --- a/lib/server/idemix/creddbaccessor_test.go +++ b/lib/server/idemix/creddbaccessor_test.go @@ -9,7 +9,6 @@ package idemix_test import ( "fmt" "testing" - "time" . "github.com/hyperledger/fabric-ca/lib/server/idemix" dmocks "github.com/hyperledger/fabric-ca/lib/server/idemix/mocks" @@ -21,7 +20,7 @@ import ( func TestInsertCredentialNilDB(t *testing.T) { credRecord := getCredRecord() - var db *dmocks.FabricCADB + var db *dmocks.DbFabricCADB accessor := NewCredentialAccessor(db, 1) err := accessor.InsertCredential(credRecord) assert.Error(t, err) @@ -30,9 +29,9 @@ func TestInsertCredentialNilDB(t *testing.T) { func TestInsertCredential(t *testing.T) { credRecord := getCredRecord() - result := new(dmocks.Result) + result := new(dmocks.SqlResult) result.On("RowsAffected").Return(int64(1), nil) - db := new(dmocks.FabricCADB) + db := new(dmocks.DbFabricCADB) db.On("NamedExec", "InsertCredential", InsertCredentialSQL, credRecord).Return(result, nil) db.On("Rebind", InsertCredentialSQL).Return(InsertCredentialSQL) accessor := NewCredentialAccessor(nil, 1) @@ -43,9 +42,9 @@ func TestInsertCredential(t *testing.T) { func TestInsertCredentialNoRowsAffected(t *testing.T) { credRecord := getCredRecord() - result := new(dmocks.Result) + result := new(dmocks.SqlResult) result.On("RowsAffected").Return(int64(0), nil) - db := new(dmocks.FabricCADB) + db := new(dmocks.DbFabricCADB) db.On("NamedExec", "InsertCredential", InsertCredentialSQL, credRecord).Return(result, nil) db.On("Rebind", InsertCredentialSQL).Return(InsertCredentialSQL) accessor := NewCredentialAccessor(db, 1) @@ -56,9 +55,9 @@ func TestInsertCredentialNoRowsAffected(t *testing.T) { func TestInsertCredentialTwoRowsAffected(t *testing.T) { credRecord := getCredRecord() - result := new(dmocks.Result) + result := new(dmocks.SqlResult) result.On("RowsAffected").Return(int64(2), nil) - db := new(dmocks.FabricCADB) + db := new(dmocks.DbFabricCADB) db.On("NamedExec", "InsertCredential", InsertCredentialSQL, credRecord).Return(result, nil) db.On("Rebind", InsertCredentialSQL).Return(InsertCredentialSQL) accessor := NewCredentialAccessor(db, 1) @@ -69,7 +68,7 @@ func TestInsertCredentialTwoRowsAffected(t *testing.T) { func TestInsertCredentialExecError(t *testing.T) { credRecord := getCredRecord() - db := new(dmocks.FabricCADB) + db := new(dmocks.DbFabricCADB) db.On("NamedExec", "InsertCredential", InsertCredentialSQL, credRecord).Return(nil, errors.New("Exec error")) db.On("Rebind", InsertCredentialSQL).Return(InsertCredentialSQL) accessor := NewCredentialAccessor(db, 1) @@ -79,7 +78,7 @@ func TestInsertCredentialExecError(t *testing.T) { } func TestGetCredentialsByIDNilDB(t *testing.T) { - var db *dmocks.FabricCADB + var db *dmocks.DbFabricCADB accessor := NewCredentialAccessor(db, 1) _, err := accessor.GetCredentialsByID("1") assert.Error(t, err) @@ -87,7 +86,7 @@ func TestGetCredentialsByIDNilDB(t *testing.T) { } func TestGetCredentialsByIDSelectError(t *testing.T) { - db := new(dmocks.FabricCADB) + db := new(dmocks.DbFabricCADB) db.On("Rebind", SelectCredentialByIDSQL).Return(SelectCredentialByIDSQL) crs := []CredRecord{} q := fmt.Sprintf(SelectCredentialByIDSQL, sqlstruct.Columns(CredRecord{})) @@ -99,7 +98,7 @@ func TestGetCredentialsByIDSelectError(t *testing.T) { } func TestGetCredentialsByID(t *testing.T) { - db := new(dmocks.FabricCADB) + db := new(dmocks.DbFabricCADB) db.On("Rebind", SelectCredentialByIDSQL).Return(SelectCredentialByIDSQL) crs := []CredRecord{} q := fmt.Sprintf(SelectCredentialByIDSQL, sqlstruct.Columns(CredRecord{})) @@ -112,7 +111,7 @@ func TestGetCredentialsByID(t *testing.T) { } func TestGetCredentialNilDB(t *testing.T) { - var db *dmocks.FabricCADB + var db *dmocks.DbFabricCADB accessor := NewCredentialAccessor(db, 1) _, err := accessor.GetCredential("1") assert.Error(t, err) @@ -120,7 +119,7 @@ func TestGetCredentialNilDB(t *testing.T) { } func TestGetCredentialSelectError(t *testing.T) { - db := new(dmocks.FabricCADB) + db := new(dmocks.DbFabricCADB) db.On("Rebind", SelectCredentialSQL).Return(SelectCredentialSQL) cr := CredRecord{} q := fmt.Sprintf(SelectCredentialSQL, sqlstruct.Columns(CredRecord{})) @@ -131,7 +130,7 @@ func TestGetCredentialSelectError(t *testing.T) { } func TestGetCredential(t *testing.T) { - db := new(dmocks.FabricCADB) + db := new(dmocks.DbFabricCADB) db.On("Rebind", SelectCredentialSQL).Return(SelectCredentialSQL) cr := CredRecord{} q := fmt.Sprintf(SelectCredentialSQL, sqlstruct.Columns(CredRecord{})) @@ -142,7 +141,7 @@ func TestGetCredential(t *testing.T) { } func TestGetRevokedCredentialsNilDB(t *testing.T) { - var db *dmocks.FabricCADB + var db *dmocks.DbFabricCADB accessor := NewCredentialAccessor(db, 1) _, err := accessor.GetRevokedCredentials() assert.Error(t, err) @@ -150,7 +149,7 @@ func TestGetRevokedCredentialsNilDB(t *testing.T) { } func TestGetRevokedCredentialsSelectError(t *testing.T) { - db := new(dmocks.FabricCADB) + db := new(dmocks.DbFabricCADB) db.On("Rebind", SelectRevokedCredentialSQL).Return(SelectRevokedCredentialSQL) q := fmt.Sprintf(SelectRevokedCredentialSQL, sqlstruct.Columns(CredRecord{})) cr := []CredRecord{} @@ -161,7 +160,7 @@ func TestGetRevokedCredentialsSelectError(t *testing.T) { } func TestGetRevokedCredentials(t *testing.T) { - db := new(dmocks.FabricCADB) + db := new(dmocks.DbFabricCADB) db.On("Rebind", SelectRevokedCredentialSQL).Return(SelectRevokedCredentialSQL) q := fmt.Sprintf(SelectRevokedCredentialSQL, sqlstruct.Columns(CredRecord{})) cr := []CredRecord{} @@ -170,28 +169,3 @@ func TestGetRevokedCredentials(t *testing.T) { _, err := accessor.GetRevokedCredentials() assert.NoError(t, err) } - -func getCredSelectFunc(t *testing.T, isError bool) func(string, interface{}, string, ...interface{}) error { - return func(funcName string, dest interface{}, query string, args ...interface{}) error { - crs := dest.(*[]CredRecord) - cr := getCredRecord() - *crs = append(*crs, cr) - if isError { - return errors.New("Failed to get credentials from DB") - } - return nil - } -} - -func getCredRecord() CredRecord { - return CredRecord{ - ID: "foo", - CALabel: "", - Expiry: time.Now(), - Level: 1, - Reason: 0, - Status: "good", - RevocationHandle: "1", - Cred: "blah", - } -} diff --git a/lib/server/idemix/cri.go b/lib/server/idemix/cri.go index 3aef6df40..766834ef9 100644 --- a/lib/server/idemix/cri.go +++ b/lib/server/idemix/cri.go @@ -7,16 +7,14 @@ SPDX-License-Identifier: Apache-2.0 package idemix import ( - proto "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric-ca/api" "github.com/hyperledger/fabric-ca/util" - "github.com/pkg/errors" ) // CRIRequestHandler is the handler for Idemix CRI (credential revocation information) request type CRIRequestHandler struct { Ctx ServerRequestCtx - Issuer MyIssuer + Issuer *IssuerInst } // HandleRequest handles processing for idemix/cri request @@ -26,15 +24,12 @@ func (ch *CRIRequestHandler) HandleRequest() (*api.GetCRIResponse, error) { return nil, err } - cri, err := ch.Issuer.RevocationAuthority().CreateCRI() + cri, err := ch.Issuer.RevocationAuthority.CreateCRI() if err != nil { return nil, err } - criBytes, err := proto.Marshal(cri) - if err != nil { - return nil, errors.New("Failed to marshal Idemix credential to bytes") - } - b64CriBytes := util.B64Encode(criBytes) + + b64CriBytes := util.B64Encode(cri) res := api.GetCRIResponse{ CRI: b64CriBytes, } diff --git a/lib/server/idemix/cri_test.go b/lib/server/idemix/cri_test.go index 3b4c4a6a9..91268c701 100644 --- a/lib/server/idemix/cri_test.go +++ b/lib/server/idemix/cri_test.go @@ -6,14 +6,11 @@ SPDX-License-Identifier: Apache-2.0 package idemix_test import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "fmt" "testing" - scheme "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" - cidemix "github.com/hyperledger/fabric-ca/lib/common/idemix" + "github.com/IBM/idemix/bccsp/types" + bccsp "github.com/IBM/idemix/bccsp/types" + ibccsp "github.com/IBM/idemix/bccsp/types" . "github.com/hyperledger/fabric-ca/lib/server/idemix" "github.com/hyperledger/fabric-ca/lib/server/idemix/mocks" "github.com/pkg/errors" @@ -31,56 +28,39 @@ func TestCRIInvalidTokenAuth(t *testing.T) { func TestCreateCRIError(t *testing.T) { ctx := new(mocks.ServerRequestCtx) ctx.On("TokenAuthentication").Return("", nil) - issuer := new(mocks.MyIssuer) + issuer := new(IssuerInst) ra := new(mocks.RevocationAuthority) ra.On("CreateCRI").Return(nil, errors.New("Failed to create CRI")) - issuer.On("RevocationAuthority").Return(ra) + issuer.RevocationAuthority = ra handler := CRIRequestHandler{Ctx: ctx, Issuer: issuer} _, err := handler.HandleRequest() assert.Error(t, err) } -func TestGetCRIMarshalError(t *testing.T) { +func TestGetCRI(t *testing.T) { ctx := new(mocks.ServerRequestCtx) ctx.On("TokenAuthentication").Return("", nil) - issuer := new(mocks.MyIssuer) + issuer := new(IssuerInst) ra := new(mocks.RevocationAuthority) - ra.On("CreateCRI").Return(nil, nil) - issuer.On("RevocationAuthority").Return(ra) - handler := CRIRequestHandler{Ctx: ctx, Issuer: issuer} - _, err := handler.HandleRequest() - assert.Error(t, err, "GetCRI should have failed when marshalling idemix.CredentialRevocationInformation") -} -func TestGetCRI(t *testing.T) { - for _, curveID := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curveID), func(t *testing.T) { - ctx := new(mocks.ServerRequestCtx) - ctx.On("TokenAuthentication").Return("", nil) - issuer := new(mocks.MyIssuer) - ra := new(mocks.RevocationAuthority) - privateKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - if err != nil { - t.Fatalf("Failed to create ecdsa key: %s", err.Error()) - } - - idemix := cidemix.InstanceForCurve(curveID) - curve := cidemix.CurveByID(curveID) + RevocationKey, err := getCSP(t).KeyGen(&bccsp.IdemixRevocationKeyGenOpts{Temporary: true}) + assert.NoError(t, err) - rand, err := curve.Rand() - if err != nil { - t.Fatalf("Failed generate random number: %s", err.Error()) - } - - cri, err := idemix.CreateCRI(privateKey, nil, 1, scheme.ALG_NO_REVOCATION, rand, idemix.Translator) - if err != nil { - t.Fatalf("Failed to create CRI: %s", err.Error()) - } - ra.On("CreateCRI").Return(cri, nil) - issuer.On("RevocationAuthority").Return(ra) - handler := CRIRequestHandler{Ctx: ctx, Issuer: issuer} - _, err = handler.HandleRequest() - assert.NoError(t, err) - }) + cri, err := getCSP(t).Sign( + RevocationKey, + nil, + &ibccsp.IdemixCRISignerOpts{ + UnrevokedHandles: nil, + Epoch: 1, + RevocationAlgorithm: types.AlgNoRevocation, + }, + ) + if err != nil { + t.Fatalf("Failed to create CRI: %s", err.Error()) } + ra.On("CreateCRI").Return(cri, nil) + issuer.RevocationAuthority = ra + handler := CRIRequestHandler{Ctx: ctx, Issuer: issuer} + _, err = handler.HandleRequest() + assert.NoError(t, err) } diff --git a/lib/server/idemix/enroll.go b/lib/server/idemix/enroll.go index 6b36ecaa2..975b2eef7 100644 --- a/lib/server/idemix/enroll.go +++ b/lib/server/idemix/enroll.go @@ -7,16 +7,14 @@ SPDX-License-Identifier: Apache-2.0 package idemix import ( + "crypto/rand" "fmt" "strconv" "strings" - idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" - math "github.com/IBM/mathlib" + bccsp "github.com/IBM/idemix/bccsp/types" "github.com/cloudflare/cfssl/log" - proto "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric-ca/api" - cidemix "github.com/hyperledger/fabric-ca/lib/common/idemix" "github.com/hyperledger/fabric-ca/lib/server/user" "github.com/hyperledger/fabric-ca/util" "github.com/pkg/errors" @@ -34,15 +32,22 @@ type EnrollmentResponse struct { Nonce string } +//go:generate mockery --name BccspBCCSP --case underscore +type BccspBCCSP interface { + bccsp.BCCSP +} + +//go:generate mockery --name BccspKey --case underscore +type BccspKey interface { + bccsp.Key +} + // EnrollRequestHandler is the handler for Idemix enroll request type EnrollRequestHandler struct { Ctx ServerRequestCtx EnrollmentID string - Issuer MyIssuer - IdmxLib Lib - CurveID cidemix.CurveID - Curve *math.Curve - Translator idemix.Translator + Issuer *IssuerInst + CSP bccsp.BCCSP } // HandleRequest handles processing for Idemix enroll @@ -59,22 +64,29 @@ func (h *EnrollRequestHandler) HandleRequest() (*EnrollmentResponse, error) { } if req.CredRequest == nil { - nonce, err := h.Issuer.NonceManager().GetNonce() + nonce, err := h.Issuer.NonceManager.GetNonce() if err != nil { return nil, errors.New("Failed to generate nonce") } resp := &EnrollmentResponse{ - Nonce: util.B64Encode(nonce.Bytes()), + Nonce: util.B64Encode(nonce), } return resp, nil } - ik, err := h.Issuer.IssuerCredential().GetIssuerKey() + isk, err := h.Issuer.IssuerCred.GetIssuerKey() if err != nil { - log.Errorf("Failed to get Idemix issuer key for the CA %s: %s", h.Issuer.Name(), err.Error()) + log.Errorf("Failed to get Idemix issuer key for the CA %s: %s", h.Issuer.Name, err.Error()) return nil, errors.WithMessage(err, fmt.Sprintf("Failed to get Idemix issuer key for the CA: %s", - h.Issuer.Name())) + h.Issuer.Name)) + } + + ipk, err := isk.PublicKey() + if err != nil { + log.Errorf("Failed to get Idemix public issuer key for the CA %s: %s", h.Issuer.Name, err.Error()) + return nil, errors.WithMessage(err, fmt.Sprintf("Failed to get Idemix public issuer key for the CA: %s", + h.Issuer.Name)) } caller, err := h.Ctx.GetCaller() @@ -83,55 +95,50 @@ func (h *EnrollRequestHandler) HandleRequest() (*EnrollmentResponse, error) { return nil, err } - nonce := h.Curve.NewZrFromBytes(req.GetIssuerNonce()) - err = h.Issuer.NonceManager().CheckNonce(nonce) + err = h.Issuer.NonceManager.CheckNonce(req.IssuerNonce) if err != nil { return nil, errors.WithMessage(err, "Invalid nonce") } - // Check the if credential request is valid - curve := cidemix.CurveByID(h.CurveID) - translator := cidemix.InstanceForCurve(h.CurveID).Translator - err = req.CredRequest.Check(ik.GetIpk(), curve, translator) - if err != nil { + valid, err := h.CSP.Verify(ipk, req.CredRequest, nil, &bccsp.IdemixCredentialRequestSignerOpts{IssuerNonce: req.IssuerNonce}) + if err != nil || !valid { log.Errorf("Invalid Idemix credential request: %s", err.Error()) return nil, errors.WithMessage(err, "Invalid Idemix credential request") } // Get revocation handle for the credential - rh, err := h.Issuer.RevocationAuthority().GetNewRevocationHandle() + rh, err := h.Issuer.RevocationAuthority.GetNewRevocationHandle() if err != nil { return nil, err } // convert the revocation handle rh to a string by first converting it to int64. - rhInt64, err := rh.Int() - if err != nil { - return nil, errors.WithMessage(err, "failed to convert RH to int64") - } - rhStr := fmt.Sprintf("%d", rhInt64) + rhStr := fmt.Sprintf("%d", rh) // Get attributes for the identity - attrMap, attrs, err := h.GetAttributeValues(caller, ik.GetIpk(), rh) + attrs, attrMap, err := h.GetAttributeValues(caller, GetAttributeNames(), rh) if err != nil { return nil, err } - cred, err := h.IdmxLib.NewCredential(ik, req.CredRequest, attrs) + credential, err := h.CSP.Sign( + isk, + req.CredRequest, + &bccsp.IdemixCredentialSignerOpts{ + Attributes: attrs, + }, + ) if err != nil { log.Errorf("Issuer '%s' failed to create new Idemix credential for identity '%s': %s", - h.Issuer.Name(), h.EnrollmentID, err.Error()) + h.Issuer.Name, h.EnrollmentID, err.Error()) return nil, errors.New("Failed to create new Idemix credential") } - credBytes, err := proto.Marshal(cred) - if err != nil { - return nil, errors.New("Failed to marshal Idemix credential to bytes") - } - b64CredBytes := util.B64Encode(credBytes) + + b64CredBytes := util.B64Encode(credential) // Store the credential in the database - err = h.Issuer.CredDBAccessor().InsertCredential(CredRecord{ - CALabel: h.Issuer.Name(), + err = h.Issuer.CredDBAccessor.InsertCredential(CredRecord{ + CALabel: h.Issuer.Name, ID: caller.GetName(), Status: "good", Cred: b64CredBytes, @@ -143,16 +150,13 @@ func (h *EnrollRequestHandler) HandleRequest() (*EnrollmentResponse, error) { } // Get CRL from revocation authority of the CA - cri, err := h.Issuer.RevocationAuthority().CreateCRI() + cri, err := h.Issuer.RevocationAuthority.CreateCRI() if err != nil { log.Errorf("Failed to generate CRI while processing idemix/credential request: %s", err.Error()) return nil, errors.New("Failed to generate CRI") } - criBytes, err := proto.Marshal(cri) - if err != nil { - return nil, errors.New("Failed to marshal CRI to bytes") - } - b64CriBytes := util.B64Encode(criBytes) + + b64CriBytes := util.B64Encode(cri) resp := &EnrollmentResponse{ Credential: b64CredBytes, Attrs: attrMap, @@ -188,39 +192,33 @@ func (h *EnrollRequestHandler) Authenticate() error { } // GenerateNonce generates a nonce for an Idemix enroll request -func (h *EnrollRequestHandler) GenerateNonce() (*math.Zr, error) { - rand, err := h.Curve.Rand() - if err != nil { - return nil, errors.Errorf("failed obtaining randomness source: %v", err) - } - x := h.Curve.NewRandomZr(rand) - x.Mod(h.Curve.GroupOrder) - return x, nil +func (h *EnrollRequestHandler) GenerateNonce() ([]byte, error) { + nonceBytes := make([]byte, 32) + _, err := rand.Read(nonceBytes) + + return nonceBytes, err } // GetAttributeValues returns attribute values of the caller of Idemix enroll request -func (h *EnrollRequestHandler) GetAttributeValues(caller user.User, ipk *idemix.IssuerPublicKey, - rh *math.Zr) (map[string]interface{}, []*math.Zr, error) { - var rc []*math.Zr +func (h *EnrollRequestHandler) GetAttributeValues(caller user.User, attributes []string, + rh int64) ([]bccsp.IdemixAttribute, map[string]interface{}, error) { attrMap := make(map[string]interface{}) - for _, attrName := range ipk.AttributeNames { + attrs := make([]bccsp.IdemixAttribute, len(attributes)) + for i, attrName := range attributes { if attrName == AttrEnrollmentID { - idBytes := []byte(caller.GetName()) - rc = append(rc, h.Curve.HashToZr(idBytes)) + attrs[i].Type = bccsp.IdemixBytesAttribute + attrs[i].Value = []byte(caller.GetName()) attrMap[attrName] = caller.GetName() } else if attrName == AttrOU { ou := append([]string{}, caller.GetAffiliationPath()...) ouVal := strings.Join(ou, ".") - ouBytes := []byte(ouVal) - rc = append(rc, h.Curve.HashToZr(ouBytes)) + attrs[i].Value = []byte(ouVal) + attrs[i].Type = bccsp.IdemixBytesAttribute attrMap[attrName] = ouVal } else if attrName == AttrRevocationHandle { - rhInt64, err := rh.Int() - if err != nil { - return nil, nil, errors.WithMessage(err, "failed to convert RH to int64") - } - rhStr := fmt.Sprintf("%d", rhInt64) - rc = append(rc, h.Curve.HashToZr([]byte(rhStr))) + rhStr := fmt.Sprintf("%d", rh) + attrs[i].Value = []byte(rhStr) + attrs[i].Type = bccsp.IdemixBytesAttribute attrMap[attrName] = rhStr } else if attrName == AttrRole { role := MEMBER.getValue() @@ -231,18 +229,13 @@ func (h *EnrollRequestHandler) GetAttributeValues(caller user.User, ipk *idemix. log.Debugf("role attribute of user %s must be a integer value", caller.GetName()) } } - rc = append(rc, h.Curve.NewZrFromInt(int64(role))) + attrs[i].Value = role + attrs[i].Type = bccsp.IdemixIntAttribute attrMap[attrName] = role } else { - attrObj, err := caller.GetAttribute(attrName) - if err != nil { - log.Errorf("Failed to get attribute %s for user %s: %s", attrName, caller.GetName(), err.Error()) - } else { - attrBytes := []byte(attrObj.GetValue()) - rc = append(rc, h.Curve.HashToZr(attrBytes)) - attrMap[attrName] = attrObj.GetValue() - } + log.Errorf("unknown attribute %s for user %s", attrName, caller.GetName()) } } - return attrMap, rc, nil + + return attrs, attrMap, nil } diff --git a/lib/server/idemix/enroll_test.go b/lib/server/idemix/enroll_test.go index 6df337e85..06a9ba6ee 100644 --- a/lib/server/idemix/enroll_test.go +++ b/lib/server/idemix/enroll_test.go @@ -6,23 +6,17 @@ SPDX-License-Identifier: Apache-2.0 package idemix_test import ( - "crypto/ecdsa" - "crypto/elliptic" "crypto/rand" - "fmt" "os" "testing" - idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" - math "github.com/IBM/mathlib" - proto "github.com/golang/protobuf/proto" + bccsp "github.com/IBM/idemix/bccsp/types" "github.com/hyperledger/fabric-ca/api" - cidemix "github.com/hyperledger/fabric-ca/lib/common/idemix" . "github.com/hyperledger/fabric-ca/lib/server/idemix" "github.com/hyperledger/fabric-ca/lib/server/idemix/mocks" - "github.com/hyperledger/fabric-ca/util" "github.com/pkg/errors" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func TestIdemixEnrollInvalidBasicAuth(t *testing.T) { @@ -56,174 +50,107 @@ func TestIdemixEnrollBadReqBody(t *testing.T) { } func TestHandleIdemixEnrollForNonce(t *testing.T) { - for _, curveID := range cidemix.Curves { - testHandleIdemixEnrollForNonce(t, curveID) - } -} - -func testHandleIdemixEnrollForNonce(t *testing.T, curveID cidemix.CurveID) { - curve := cidemix.CurveByID(curveID) ctx := new(mocks.ServerRequestCtx) ctx.On("BasicAuthentication").Return("foo", nil) - idemixlib := new(mocks.Lib) - rand, err := curve.Rand() - if err != nil { - t.Fatalf("Error generating a random number") - } + idemixlib := new(mocks.BccspBCCSP) - rmo := curve.NewRandomZr(rand) - rmo.Mod(curve.GroupOrder) - idemixlib.On("GetRand").Return(rand, nil) - idemixlib.On("RandModOrder", rand).Return(rmo) ctx.On("IsBasicAuth").Return(true) req := api.IdemixEnrollmentRequestNet{} req.CredRequest = nil ctx.On("ReadBody", &req).Return(nil) - issuer := new(mocks.MyIssuer) - issuer.On("IdemixRand").Return(rand) + issuer := new(IssuerInst) + + nonceBytes := make([]byte, 32) + _, err := rand.Read(nonceBytes) + assert.NoError(t, err) nm := new(mocks.NonceManager) - nm.On("GetNonce").Return(curve.NewRandomZr(rand), nil) - issuer.On("NonceManager").Return(nm) - handler := EnrollRequestHandler{Ctx: ctx, Issuer: issuer, IdmxLib: idemixlib} + nm.On("GetNonce").Return(nonceBytes, nil) + issuer.NonceManager = nm + handler := EnrollRequestHandler{Ctx: ctx, Issuer: issuer, CSP: idemixlib} _, err = handler.HandleRequest() assert.NoError(t, err, "Idemix enroll should return a valid nonce") } func TestHandleIdemixEnrollForNonceTokenAuth(t *testing.T) { - for _, curveID := range cidemix.Curves { - testHandleIdemixEnrollForNonceTokenAuth(t, curveID) - } -} - -func testHandleIdemixEnrollForNonceTokenAuth(t *testing.T, curveID cidemix.CurveID) { - curve := cidemix.CurveByID(curveID) - ctx := new(mocks.ServerRequestCtx) ctx.On("TokenAuthentication").Return("foo", nil) - idemixlib := new(mocks.Lib) - rand, err := curve.Rand() - if err != nil { - t.Fatalf("Error generating a random number") - } - - rmo := curve.NewRandomZr(rand) - rmo.Mod(curve.GroupOrder) - - idemixlib.On("GetRand").Return(rand, nil) - idemixlib.On("RandModOrder", rand).Return(rmo) + idemixlib := new(mocks.BccspBCCSP) + nonceBytes := make([]byte, 32) + _, err := rand.Read(nonceBytes) + assert.NoError(t, err) ctx.On("IsBasicAuth").Return(false) req := api.IdemixEnrollmentRequestNet{} req.CredRequest = nil ctx.On("ReadBody", &req).Return(nil) - issuer := new(mocks.MyIssuer) - issuer.On("IdemixRand").Return(rand) + issuer := new(IssuerInst) nm := new(mocks.NonceManager) - nm.On("GetNonce").Return(curve.NewRandomZr(rand), nil) - issuer.On("NonceManager").Return(nm) + nm.On("GetNonce").Return(nonceBytes, nil) + issuer.NonceManager = nm ctx.On("GetIssuer").Return(issuer, nil) - handler := EnrollRequestHandler{Ctx: ctx, IdmxLib: idemixlib, Issuer: issuer} + handler := EnrollRequestHandler{Ctx: ctx, CSP: idemixlib, Issuer: issuer} _, err = handler.HandleRequest() assert.NoError(t, err, "Idemix enroll should return a valid nonce") } func TestHandleIdemixEnrollForNonceError(t *testing.T) { - for _, curveID := range cidemix.Curves { - testHandleIdemixEnrollForNonceError(t, curveID) - } -} - -func testHandleIdemixEnrollForNonceError(t *testing.T, curveID cidemix.CurveID) { - curve := cidemix.CurveByID(curveID) ctx := new(mocks.ServerRequestCtx) ctx.On("TokenAuthentication").Return("foo", nil) - idemixlib := new(mocks.Lib) - rand, err := curve.Rand() - if err != nil { - t.Fatalf("Error generating a random number") - } - - rmo := curve.NewRandomZr(rand) - rmo.Mod(curve.GroupOrder) - - idemixlib.On("GetRand").Return(rand, nil) - idemixlib.On("RandModOrder", rand).Return(rmo) + idemixlib := new(mocks.BccspBCCSP) ctx.On("IsBasicAuth").Return(false) req := api.IdemixEnrollmentRequestNet{} req.CredRequest = nil ctx.On("ReadBody", &req).Return(nil) - issuer := new(mocks.MyIssuer) - issuer.On("IdemixRand").Return(rand) + issuer := new(IssuerInst) nm := new(mocks.NonceManager) nm.On("GetNonce").Return(nil, errors.New("Failed to generate nonce")) - issuer.On("NonceManager").Return(nm) + issuer.NonceManager = nm ctx.On("GetIssuer").Return(issuer, nil) - handler := EnrollRequestHandler{Ctx: ctx, IdmxLib: idemixlib, Issuer: issuer} - _, err = handler.HandleRequest() + handler := EnrollRequestHandler{Ctx: ctx, CSP: idemixlib, Issuer: issuer} + _, err := handler.HandleRequest() assert.Error(t, err, "Idemix enroll should return an error because NonceManager.GetNonce returned an error") } func TestHandleIdemixEnrollForCredentialError(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testHandleIdemixEnrollForCredentialError(t, curve) - }) - } -} - -func testHandleIdemixEnrollForCredentialError(t *testing.T, curveID cidemix.CurveID) { - curve := cidemix.CurveByID(curveID) - ctx := new(mocks.ServerRequestCtx) ctx.On("BasicAuthentication").Return("foo", nil) - idemixlib := new(mocks.Lib) - rand, err := curve.Rand() - if err != nil { - t.Fatalf("Error generating a random number") - } - - rmo := curve.NewRandomZr(rand) - rmo.Mod(curve.GroupOrder) + idemixlib := getCSP(t) - idemixlib.On("GetRand").Return(rand, nil) - idemixlib.On("RandModOrder", rand).Return(rmo, nil) - - testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) - issuerCred := NewIssuerCredential(testPublicKeyFile, testSecretKeyFile, idemixlib, curveID) - issuer := new(mocks.MyIssuer) - issuer.On("Name").Return("") - issuer.On("IssuerCredential").Return(issuerCred) - issuer.On("IdemixRand").Return(rand) + issuerCred := NewIssuerCredential(testPublicKeyFile, testSecretKeyFile, idemixlib) + issuer := new(IssuerInst) + issuer.Name = "" + issuer.IssuerCred = issuerCred ctx.On("GetIssuer").Return(issuer, nil) ctx.On("IsBasicAuth").Return(true) - handler := EnrollRequestHandler{Ctx: ctx, IdmxLib: idemixlib, Issuer: issuer, CurveID: curveID, Curve: curve, Translator: cidemix.InstanceForCurve(curveID).Translator} + handler := EnrollRequestHandler{Ctx: ctx, CSP: idemixlib, Issuer: issuer} nonce, err := handler.GenerateNonce() if err != nil { t.Fatalf("Failed to generate nonce: %s", err.Error()) } - credReq, _, err := newIdemixCredentialRequest(t, nonce, curveID, testPublicKeyFile, testSecretKeyFile) + credReq, _ := newIdemixCredentialRequest(t, nonce, testPublicKeyFile, testSecretKeyFile) if err != nil { t.Fatalf("Failed to create credential request: %s", err.Error()) } - f := getReadBodyFunc(t, credReq) + f := getReadBodyFunc(t, credReq, nonce) req := api.IdemixEnrollmentRequestNet{} ctx.On("ReadBody", &req).Return(f) @@ -246,70 +173,48 @@ func testHandleIdemixEnrollForCredentialError(t *testing.T, curveID cidemix.Curv } func TestHandleIdemixEnrollCheckNonceError(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - tstHandleIdemixEnrollCheckNonceError(t, curve) - }) - } -} - -func tstHandleIdemixEnrollCheckNonceError(t *testing.T, curveID cidemix.CurveID) { - curve := cidemix.CurveByID(curveID) - ctx := new(mocks.ServerRequestCtx) - idemixlib := new(mocks.Lib) - rand, err := curve.Rand() - if err != nil { - t.Fatalf("Error generating a random number") - } - - rmo := curve.NewRandomZr(rand) - rmo.Mod(curve.GroupOrder) - idemixlib.On("GetRand").Return(rand, nil) - idemixlib.On("RandModOrder", rand).Return(rmo) - - testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) - issuerCred := NewIssuerCredential(testPublicKeyFile, - testSecretKeyFile, idemixlib, curveID) + issuerCred := NewIssuerCredential(testPublicKeyFile, testSecretKeyFile, getCSP(t)) err = issuerCred.Load() if err != nil { t.Fatalf("Failed to load issuer credential") } - rh := curve.NewZrFromInt(1) + rh := int64(1) ra := new(mocks.RevocationAuthority) ra.On("GetNewRevocationHandle").Return(rh, nil) - issuer := new(mocks.MyIssuer) - issuer.On("Name").Return("") - issuer.On("IssuerCredential").Return(issuerCred) - issuer.On("IdemixRand").Return(rand) - issuer.On("RevocationAuthority").Return(ra) + issuer := new(IssuerInst) + issuer.Name = "" + issuer.IssuerCred = issuerCred + issuer.RevocationAuthority = ra ctx.On("IsBasicAuth").Return(true) - handler := EnrollRequestHandler{Ctx: ctx, IdmxLib: idemixlib, Issuer: issuer, CurveID: curveID, Curve: curve, Translator: cidemix.InstanceForCurve(curveID).Translator} + handler := EnrollRequestHandler{Ctx: ctx, CSP: getCSP(t), Issuer: issuer} nm := new(mocks.NonceManager) - nonce := curve.NewRandomZr(rand) - nonce.Mod(curve.GroupOrder) + nonceBytes := make([]byte, 32) + _, err = rand.Read(nonceBytes) + assert.NoError(t, err) - nm.On("GetNonce").Return(nonce, nil) - nm.On("CheckNonce", nonce).Return(errors.New("Invalid nonce")) - issuer.On("NonceManager").Return(nm) + nm.On("GetNonce").Return(nonceBytes, nil) + nm.On("CheckNonce", nonceBytes).Return(errors.New("Invalid nonce")) + issuer.NonceManager = nm - caller := new(mocks.User) + caller := new(mocks.UserUser) caller.On("Name").Return("foo") - credReq, _, err := newIdemixCredentialRequest(t, nonce, curveID, testPublicKeyFile, testSecretKeyFile) + credReq, _ := newIdemixCredentialRequest(t, nonceBytes, testPublicKeyFile, testSecretKeyFile) if err != nil { t.Fatalf("Failed to create test credential request") } ctx.On("BasicAuthentication").Return("foo", nil) - f := getReadBodyFunc(t, credReq) + f := getReadBodyFunc(t, credReq, nonceBytes) ctx.On("ReadBody", &api.IdemixEnrollmentRequestNet{}).Return(f) ctx.On("GetCA").Return(issuer, nil) ctx.On("GetCaller").Return(caller, nil) @@ -320,164 +225,123 @@ func tstHandleIdemixEnrollCheckNonceError(t *testing.T, curveID cidemix.CurveID) } func TestHandleIdemixEnrollNewCredError(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testHandleIdemixEnrollNewCredError(t, curve) - }) - } -} - -func testHandleIdemixEnrollNewCredError(t *testing.T, curveID cidemix.CurveID) { ctx := new(mocks.ServerRequestCtx) - idemixlib := new(mocks.Lib) - curve := cidemix.CurveByID(curveID) - rnd, err := curve.Rand() - if err != nil { - t.Fatalf("Error generating a random number") - } - rmo := curve.NewRandomZr(rnd) - rmo.Mod(curve.GroupOrder) - idemixlib.On("GetRand").Return(rnd, nil) - idemixlib.On("RandModOrder", rnd).Return(rmo) - testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) issuerCred := NewIssuerCredential(testPublicKeyFile, - testSecretKeyFile, idemixlib, curveID) + testSecretKeyFile, getCSP(t)) err = issuerCred.Load() if err != nil { t.Fatalf("Failed to load issuer credential") } - ik, _ := issuerCred.GetIssuerKey() - rh := curve.NewZrFromInt(1) + rh := int64(1) ra := new(mocks.RevocationAuthority) ra.On("GetNewRevocationHandle").Return(rh, nil) - issuer := new(mocks.MyIssuer) - issuer.On("Name").Return("") - issuer.On("IssuerCredential").Return(issuerCred) - issuer.On("IdemixRand").Return(rnd) - issuer.On("RevocationAuthority").Return(ra) + issuer := new(IssuerInst) + issuer.Name = "" + issuer.IssuerCred = issuerCred + issuer.RevocationAuthority = ra ctx.On("IsBasicAuth").Return(true) - handler := EnrollRequestHandler{Ctx: ctx, IdmxLib: idemixlib, Issuer: issuer, CurveID: curveID, Curve: curve, Translator: cidemix.InstanceForCurve(curveID).Translator} + handler := EnrollRequestHandler{Ctx: ctx, CSP: getCSP(t), Issuer: issuer} nm := new(mocks.NonceManager) - rand, err := curve.Rand() - if err != nil { - t.Fatalf("Failed to create randomness source") - } - nonce := curve.NewRandomZr(rand) - nonce.Mod(curve.GroupOrder) + nonceBytes := make([]byte, 32) + _, err = rand.Read(nonceBytes) + assert.NoError(t, err) - nm.On("GetNonce").Return(nonce, nil) - nm.On("CheckNonce", nonce).Return(nil) - issuer.On("NonceManager").Return(nm) + nm.On("GetNonce").Return(nonceBytes, nil) + nm.On("CheckNonce", nonceBytes).Return(nil) + issuer.NonceManager = nm - caller := new(mocks.User) + caller := new(mocks.UserUser) caller.On("GetName").Return("foo") caller.On("GetAffiliationPath").Return([]string{"a", "b", "c"}) caller.On("GetAttribute", "role").Return(&api.Attribute{Name: "role", Value: "2"}, nil) caller.On("LoginComplete").Return(nil) - credReq, _, err := newIdemixCredentialRequest(t, nonce, curveID, testPublicKeyFile, testSecretKeyFile) + credReq, _ := newIdemixCredentialRequest(t, nonceBytes, testPublicKeyFile, testSecretKeyFile) if err != nil { t.Fatalf("Failed to create test credential request") } - _, attrs, err := handler.GetAttributeValues(caller, ik.Ipk, rh) - if err != nil { - t.Fatalf("Failed to get attributes") - } - - idemixlib.On("NewCredential", ik, credReq, attrs).Return(nil, errors.New("Failed to create credential")) - ctx.On("BasicAuthentication").Return("foo", nil) - f := getReadBodyFunc(t, credReq) + f := getReadBodyFunc(t, credReq, nonceBytes) ctx.On("ReadBody", &api.IdemixEnrollmentRequestNet{}).Return(f) ctx.On("GetCA").Return(issuer, nil) ctx.On("GetCaller").Return(caller, nil) + mockCsp := new(mocks.BccspBCCSP) + mockCsp.On("Verify", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(true, nil) + mockCsp.On("Sign", mock.Anything, mock.Anything, mock.Anything).Return(nil, errors.New("error error")) + handler.CSP = mockCsp + // Now setup of all mocks is over, test the method _, err = handler.HandleRequest() assert.Error(t, err, "Idemix enroll should return error because idemix.NewCredential returned error") } func TestHandleIdemixEnrollInsertCredError(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testHandleIdemixEnrollInsertCredError(t, curve) - }) - } -} - -func testHandleIdemixEnrollInsertCredError(t *testing.T, curveID cidemix.CurveID) { - curve := cidemix.CurveByID(curveID) - rand, err := curve.Rand() - if err != nil { - t.Fatalf("Failed to create randomness source") - } - - rmo := curve.NewRandomZr(rand) - rmo.Mod(curve.GroupOrder) - ctx := new(mocks.ServerRequestCtx) - idemixlib := new(mocks.Lib) - idemixlib.On("RandModOrder", rmo).Return(rmo) - testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) issuerCred := NewIssuerCredential(testPublicKeyFile, - testSecretKeyFile, idemixlib, curveID) + testSecretKeyFile, getCSP(t)) err = issuerCred.Load() if err != nil { t.Fatalf("Failed to load issuer credential") } - ik, _ := issuerCred.GetIssuerKey() + isk, _ := issuerCred.GetIssuerKey() - rh := curve.NewZrFromInt(1) + rh := int64(1) ra := new(mocks.RevocationAuthority) ra.On("GetNewRevocationHandle").Return(rh, nil) - issuer := new(mocks.MyIssuer) - issuer.On("Name").Return("") - issuer.On("IssuerCredential").Return(issuerCred) - issuer.On("RevocationAuthority").Return(ra) + issuer := new(IssuerInst) + issuer.Name = "" + issuer.IssuerCred = issuerCred + issuer.RevocationAuthority = ra ctx.On("IsBasicAuth").Return(true) - handler := EnrollRequestHandler{Ctx: ctx, IdmxLib: idemixlib, Issuer: issuer, CurveID: curveID, Curve: curve, Translator: cidemix.InstanceForCurve(curveID).Translator} + handler := EnrollRequestHandler{Ctx: ctx, Issuer: issuer} nm := new(mocks.NonceManager) - nonce := curve.NewRandomZr(rand) - nonce.Mod(curve.GroupOrder) + nonceBytes := make([]byte, 32) + _, err = rand.Read(nonceBytes) + assert.NoError(t, err) - nm.On("GetNonce").Return(nonce, nil) - nm.On("CheckNonce", nonce).Return(nil) + nm.On("GetNonce").Return(nonceBytes, nil) + nm.On("CheckNonce", nonceBytes).Return(nil) - caller := new(mocks.User) + caller := new(mocks.UserUser) caller.On("GetName").Return("foo") caller.On("GetAffiliationPath").Return([]string{"a", "b", "c"}) caller.On("GetAttribute", "role").Return(&api.Attribute{Name: "role", Value: "2"}, nil) caller.On("LoginComplete").Return(nil) - credReq, _, err := newIdemixCredentialRequest(t, nonce, curveID, testPublicKeyFile, testSecretKeyFile) - if err != nil { - t.Fatalf("Failed to create test credential request") - } - _, attrs, err := handler.GetAttributeValues(caller, ik.Ipk, rh) + credReq, _ := newIdemixCredentialRequest(t, nonceBytes, testPublicKeyFile, testSecretKeyFile) + attrs, _, err := handler.GetAttributeValues(caller, GetAttributeNames(), int64(rh)) if err != nil { t.Fatalf("Failed to get attributes") } - cred, err := cidemix.InstanceForCurve(curveID).NewCredential(ik, credReq, attrs, rand, cidemix.InstanceForCurve(curveID).Translator) + cred, err := getCSP(t).Sign( + isk, + credReq, + &bccsp.IdemixCredentialSignerOpts{ + Attributes: attrs, + }, + ) if err != nil { t.Fatalf("Failed to create credential: %v", err) } - idemixlib.On("NewCredential", ik, credReq, attrs).Return(cred, nil) b64CredBytes, err := getB64EncodedCred(cred) if err != nil { @@ -491,95 +355,83 @@ func testHandleIdemixEnrollInsertCredError(t *testing.T, curveID cidemix.CurveID Cred: b64CredBytes, }).Return(errors.New("Failed to add credential to DB")) - issuer.On("CredDBAccessor").Return(credAccessor, nil) - issuer.On("NonceManager").Return(nm) + issuer.CredDBAccessor = credAccessor + issuer.NonceManager = nm ctx.On("BasicAuthentication").Return("foo", nil) - f := getReadBodyFunc(t, credReq) + f := getReadBodyFunc(t, credReq, nonceBytes) ctx.On("ReadBody", &api.IdemixEnrollmentRequestNet{}).Return(f) ctx.On("GetCA").Return(issuer, nil) ctx.On("GetCaller").Return(caller, nil) + mockcsp := new(mocks.BccspBCCSP) + handler.CSP = mockcsp + mockcsp.On("Verify", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(true, nil) + mockcsp.On("Sign", mock.Anything, mock.Anything, mock.Anything).Return(cred, nil) + // Now setup of all mocks is over, test the method _, err = handler.HandleRequest() assert.Error(t, err, "Idemix enroll should return error because CredDBAccessor.InsertCredentail returned error") } func TestHandleIdemixEnrollForCredentialSuccess(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testHandleIdemixEnrollForCredentialSuccess(t, curve) - }) - } -} - -func testHandleIdemixEnrollForCredentialSuccess(t *testing.T, curveID cidemix.CurveID) { - curve := cidemix.CurveByID(curveID) - rand, err := curve.Rand() - if err != nil { - t.Fatalf("Failed to create randomness source") - } - ctx := new(mocks.ServerRequestCtx) - idemixlib := new(mocks.Lib) - rmo := curve.NewRandomZr(rand) - rmo.Mod(curve.GroupOrder) + idemixlib := getCSP(t) - idemixlib.On("GetRand").Return(rand, nil) - idemixlib.On("RandModOrder", rand).Return(rmo) - - testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t) + assert.NoError(t, err) defer os.RemoveAll(tmpDir) issuerCred := NewIssuerCredential(testPublicKeyFile, - testSecretKeyFile, idemixlib, curveID) + testSecretKeyFile, idemixlib) err = issuerCred.Load() if err != nil { t.Fatalf("Failed to load issuer credential") } - ik, _ := issuerCred.GetIssuerKey() + isk, _ := issuerCred.GetIssuerKey() - rh := curve.NewZrFromInt(1) + rh := int64(1) ra := new(mocks.RevocationAuthority) ra.On("GetNewRevocationHandle").Return(rh, nil) - issuer := new(mocks.MyIssuer) - issuer.On("Name").Return("") - issuer.On("IssuerCredential").Return(issuerCred) - issuer.On("IdemixRand").Return(rand) - issuer.On("RevocationAuthority").Return(ra) + issuer := new(IssuerInst) + issuer.Name = "" + issuer.IssuerCred = issuerCred + issuer.RevocationAuthority = ra ctx.On("IsBasicAuth").Return(true) - handler := EnrollRequestHandler{Ctx: ctx, IdmxLib: idemixlib, Issuer: issuer, CurveID: curveID, Curve: curve, Translator: cidemix.InstanceForCurve(curveID).Translator} + handler := EnrollRequestHandler{Ctx: ctx, CSP: idemixlib, Issuer: issuer} nm := new(mocks.NonceManager) - nonce := curve.NewRandomZr(rand) - nonce.Mod(curve.GroupOrder) - nm.On("GetNonce").Return(nonce, nil) - nm.On("CheckNonce", nonce).Return(nil) + nonceBytes := make([]byte, 32) + _, err = rand.Read(nonceBytes) + assert.NoError(t, err) + + nm.On("GetNonce").Return(nonceBytes, nil) + nm.On("CheckNonce", nonceBytes).Return(nil) - caller := new(mocks.User) + caller := new(mocks.UserUser) caller.On("GetName").Return("foo") caller.On("GetAffiliationPath").Return([]string{"a", "b", "c"}) caller.On("GetAttribute", "role").Return(&api.Attribute{Name: "role", Value: "2"}, nil) caller.On("LoginComplete").Return(nil) - credReq, _, err := newIdemixCredentialRequest(t, nonce, curveID, testPublicKeyFile, testSecretKeyFile) - if err != nil { - t.Fatalf("Failed to create test credential request") - } - _, attrs, err := handler.GetAttributeValues(caller, ik.Ipk, rh) + credReq, _ := newIdemixCredentialRequest(t, nonceBytes, testPublicKeyFile, testSecretKeyFile) + attrs, _, err := handler.GetAttributeValues(caller, GetAttributeNames(), 1) if err != nil { t.Fatalf("Failed to get attributes") } - idemix := cidemix.InstanceForCurve(curveID) - - cred, err := idemix.NewCredential(ik, credReq, attrs, rand, idemix.Translator) + cred, err := getCSP(t).Sign( + isk, + credReq, + &bccsp.IdemixCredentialSignerOpts{ + Attributes: attrs, + }, + ) if err != nil { t.Fatalf("Failed to create credential: %v", err) } - idemixlib.On("NewCredential", ik, credReq, attrs).Return(cred, nil) b64CredBytes, err := getB64EncodedCred(cred) if err != nil { @@ -587,123 +439,47 @@ func testHandleIdemixEnrollForCredentialSuccess(t *testing.T, curveID cidemix.Cu } credAccessor := new(mocks.CredDBAccessor) credAccessor.On("InsertCredential", CredRecord{ - RevocationHandle: "1", - CALabel: "", ID: "foo", Status: "good", Cred: b64CredBytes, + RevocationHandle: "1", ID: "foo", Status: "good", Cred: b64CredBytes, }).Return(nil) - issuer.On("CredDBAccessor").Return(credAccessor, nil) - issuer.On("NonceManager").Return(nm) + issuer.CredDBAccessor = credAccessor + issuer.NonceManager = nm - cri, err := createCRI(t, curveID) + cri, err := createCRI(t) if err != nil { t.Fatalf("Failed to create CRI: %s", err.Error()) } ra.On("CreateCRI").Return(cri, nil) ctx.On("BasicAuthentication").Return("foo", nil) - f := getReadBodyFunc(t, credReq) + f := getReadBodyFunc(t, credReq, nonceBytes) ctx.On("ReadBody", &api.IdemixEnrollmentRequestNet{}).Return(f) ctx.On("GetCA").Return(issuer, nil) ctx.On("GetCaller").Return(caller, nil) + mockcsp := new(mocks.BccspBCCSP) + handler.CSP = mockcsp + mockcsp.On("Verify", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(true, nil) + mockcsp.On("Sign", mock.Anything, mock.Anything, mock.Anything).Return(cred, nil) + // Now setup of all mocks is over, test the method _, err = handler.HandleRequest() assert.NoError(t, err) } func TestGetAttributeValues(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testGetAttributeValues(t, curve) - }) - } -} - -func testGetAttributeValues(t *testing.T, curveID cidemix.CurveID) { - curve := cidemix.CurveByID(curveID) - ctx := new(mocks.ServerRequestCtx) - idemixlib := new(mocks.Lib) + idemixlib := new(mocks.BccspBCCSP) ctx.On("IsBasicAuth").Return(true) - handler := EnrollRequestHandler{Ctx: ctx, IdmxLib: idemixlib, CurveID: curveID, Curve: curve, Translator: cidemix.InstanceForCurve(curveID).Translator} + handler := EnrollRequestHandler{Ctx: ctx, CSP: idemixlib} - caller := new(mocks.User) + caller := new(mocks.UserUser) caller.On("GetName").Return("foo") caller.On("GetAffiliationPath").Return([]string{"a", "b", "c"}) caller.On("GetAttribute", "role").Return(&api.Attribute{Name: "role", Value: "2"}, nil) caller.On("GetAttribute", "type").Return(&api.Attribute{Name: "type", Value: "client"}, nil) caller.On("LoginComplete").Return(nil) - rh := curve.NewZrFromInt(1) - - attrNames := GetAttributeNames() - attrNames = append(attrNames, "type") - ipk := idemix.IssuerPublicKey{AttributeNames: attrNames} - _, _, err := handler.GetAttributeValues(caller, &ipk, rh) + _, _, err := handler.GetAttributeValues(caller, GetAttributeNames(), 1) assert.NoError(t, err) } - -func createCRI(t *testing.T, curveID cidemix.CurveID) (*idemix.CredentialRevocationInformation, error) { - key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - if err != nil { - return nil, err - } - - curve := cidemix.CurveByID(curveID) - - rnd, err := curve.Rand() - if err != nil { - return nil, err - } - - idemixInstance := cidemix.InstanceForCurve(curveID) - - return idemixInstance.CreateCRI(key, []*math.Zr{}, 1, idemix.ALG_NO_REVOCATION, rnd, idemixInstance.Translator) -} - -func getB64EncodedCred(cred *idemix.Credential) (string, error) { - credBytes, err := proto.Marshal(cred) - if err != nil { - return "", errors.New("Failed to marshal credential to bytes") - } - b64CredBytes := util.B64Encode(credBytes) - return b64CredBytes, nil -} - -func getReadBodyFunc(t *testing.T, credReq *idemix.CredRequest) func(body interface{}) error { - return func(body interface{}) error { - enrollReq, _ := body.(*api.IdemixEnrollmentRequestNet) - if credReq == nil { - return errors.New("Error reading the body") - } - enrollReq.CredRequest = credReq - return nil - } -} - -func newIdemixCredentialRequest(t *testing.T, nonce *math.Zr, curveID cidemix.CurveID, testPublicKeyFile, testSecretKeyFile string) (*idemix.CredRequest, *math.Zr, error) { - idmxlib := new(mocks.Lib) - issuerCred := NewIssuerCredential(testPublicKeyFile, testSecretKeyFile, idmxlib, curveID) - err := issuerCred.Load() - if err != nil { - t.Fatalf("Failed to load issuer credential") - } - ik, err := issuerCred.GetIssuerKey() - if err != nil { - t.Fatalf("Issuer credential returned error while getting issuer key") - } - curve := cidemix.InstanceForCurve(curveID).Curve - - rand, err := curve.Rand() - if err != nil { - return nil, nil, err - } - - sk := curve.NewRandomZr(rand) - sk.Mod(curve.GroupOrder) - - idemix := cidemix.InstanceForCurve(curveID) - - credReq, err := idemix.NewCredRequest(sk, nonce.Bytes(), ik.Ipk, rand, idemix.Translator) - return credReq, sk, err -} diff --git a/lib/server/idemix/idemixlib.go b/lib/server/idemix/idemixlib.go index 4ae49d848..39e87216d 100644 --- a/lib/server/idemix/idemixlib.go +++ b/lib/server/idemix/idemixlib.go @@ -1,113 +1,114 @@ -/* -Copyright IBM Corp. All Rights Reserved. +// /* +// Copyright IBM Corp. All Rights Reserved. -SPDX-License-Identifier: Apache-2.0 -*/ +// SPDX-License-Identifier: Apache-2.0 +// */ package idemix -import ( - "crypto/ecdsa" - "fmt" - - idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" - math "github.com/IBM/mathlib" - cidemix "github.com/hyperledger/fabric-ca/lib/common/idemix" - "github.com/pkg/errors" -) - -// Lib represents idemix library -type Lib interface { - NewIssuerKey(AttributeNames []string) (ik *idemix.IssuerKey, err error) - NewCredential(key *idemix.IssuerKey, m *idemix.CredRequest, attrs []*math.Zr) (cred *idemix.Credential, err error) - CreateCRI(key *ecdsa.PrivateKey, unrevokedHandles []*math.Zr, epoch int, alg idemix.RevocationAlgorithm) (cri *idemix.CredentialRevocationInformation, err error) - GenerateLongTermRevocationKey() (pk *ecdsa.PrivateKey, err error) - RandModOrder() (*math.Zr, error) -} - -// libImpl is adapter for idemix library. It implements Lib interface -type libImpl struct { - idemix *idemix.Idemix - curve *math.Curve -} - -// NewLib returns an instance of an object that implements Lib interface -func NewLib(curveID cidemix.CurveID) Lib { - return &libImpl{idemix: cidemix.InstanceForCurve(curveID), curve: cidemix.CurveByID(curveID)} -} - -func (i *libImpl) NewCredential(key *idemix.IssuerKey, m *idemix.CredRequest, attrs []*math.Zr) (cred *idemix.Credential, err error) { - defer func() { - r := recover() - if r != nil { - err = errors.Errorf("failure: %s", r) - } - }() - - rand, err := i.curve.Rand() - if err != nil { - return nil, errors.Errorf("failed obtaining randomness source: %v", err) - } - - return i.idemix.NewCredential(key, m, attrs, rand, i.idemix.Translator) -} - -func (i *libImpl) RandModOrder() (zr *math.Zr, err error) { - defer func() { - r := recover() - if r != nil { - fmt.Printf("##########################") - err = errors.Errorf("failure: %s", r) - } - }() - - rand, err := i.curve.Rand() - if err != nil { - return nil, errors.Errorf("failed obtaining randomness source: %v", err) - } - x := i.curve.NewRandomZr(rand) - x.Mod(i.curve.GroupOrder) - - return x, nil -} - -func (i *libImpl) NewIssuerKey(attributeNames []string) (ik *idemix.IssuerKey, err error) { - defer func() { - r := recover() - if r != nil { - err = errors.Errorf("failure: %s", r) - } - }() - - rand, err := i.curve.Rand() - if err != nil { - return nil, errors.Errorf("failed to obtain randomness source: %v", err) - } - return i.idemix.NewIssuerKey(attributeNames, rand, i.idemix.Translator) -} - -func (i *libImpl) CreateCRI(key *ecdsa.PrivateKey, unrevokedHandles []*math.Zr, epoch int, alg idemix.RevocationAlgorithm) (cri *idemix.CredentialRevocationInformation, err error) { - defer func() { - r := recover() - if r != nil { - err = errors.Errorf("failure: %s", r) - } - }() - - rand, err := i.curve.Rand() - if err != nil { - return nil, errors.Errorf("failed to obtain randomness source: %v", err) - } - - return i.idemix.CreateCRI(key, unrevokedHandles, epoch, alg, rand, i.idemix.Translator) -} - -func (i *libImpl) GenerateLongTermRevocationKey() (pk *ecdsa.PrivateKey, err error) { - defer func() { - r := recover() - if r != nil { - err = errors.Errorf("failure: %s", r) - } - }() - return i.idemix.GenerateLongTermRevocationKey() -} +// import ( +// "crypto/ecdsa" +// "fmt" + +// idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" +// math "github.com/IBM/mathlib" +// cidemix "github.com/hyperledger/fabric-ca/lib/common/idemix" +// scheme "github.com/hyperledger/fabric/idemix" +// "github.com/pkg/errors" +// ) + +// // Lib represents idemix library +// type Lib interface { +// NewIssuerKey(AttributeNames []string) (ik *idemix.IssuerKey, err error) +// NewCredential(key *idemix.IssuerKey, m *idemix.CredRequest, attrs []*math.Zr) (cred *idemix.Credential, err error) +// CreateCRI(key *ecdsa.PrivateKey, unrevokedHandles []*math.Zr, epoch int, alg idemix.RevocationAlgorithm) (cri *idemix.CredentialRevocationInformation, err error) +// GenerateLongTermRevocationKey() (pk *ecdsa.PrivateKey, err error) +// RandModOrder() (*math.Zr, error) +// } + +// // libImpl is adapter for idemix library. It implements Lib interface +// type libImpl struct { +// idemix *idemix.Idemix +// curve *math.Curve +// } + +// // NewLib returns an instance of an object that implements Lib interface +// func NewLib(curveID cidemix.CurveID) Lib { +// return &libImpl{idemix: cidemix.InstanceForCurve(curveID), curve: cidemix.CurveByID(curveID)} +// } + +// func (i *libImpl) NewCredential(key *idemix.IssuerKey, m *idemix.CredRequest, attrs []*math.Zr) (cred *idemix.Credential, err error) { +// defer func() { +// r := recover() +// if r != nil { +// err = errors.Errorf("failure: %s", r) +// } +// }() + +// rand, err := i.curve.Rand() +// if err != nil { +// return nil, errors.Errorf("failed obtaining randomness source: %v", err) +// } + +// return i.idemix.NewCredential(key, m, attrs, rand, i.idemix.Translator) +// } + +// func (i *libImpl) RandModOrder() (zr *math.Zr, err error) { +// defer func() { +// r := recover() +// if r != nil { +// fmt.Printf("##########################") +// err = errors.Errorf("failure: %s", r) +// } +// }() + +// rand, err := i.curve.Rand() +// if err != nil { +// return nil, errors.Errorf("failed obtaining randomness source: %v", err) +// } +// x := i.curve.NewRandomZr(rand) +// x.Mod(i.curve.GroupOrder) + +// return x, nil +// } + +// func (i *libImpl) NewIssuerKey(attributeNames []string) (ik *idemix.IssuerKey, err error) { +// defer func() { +// r := recover() +// if r != nil { +// err = errors.Errorf("failure: %s", r) +// } +// }() + +// rand, err := i.curve.Rand() +// if err != nil { +// return nil, errors.Errorf("failed to obtain randomness source: %v", err) +// } +// return i.idemix.NewIssuerKey(attributeNames, rand, i.idemix.Translator) +// } + +// func (i *libImpl) CreateCRI(key *ecdsa.PrivateKey, unrevokedHandles []*math.Zr, epoch int, alg idemix.RevocationAlgorithm) (cri *idemix.CredentialRevocationInformation, err error) { +// defer func() { +// r := recover() +// if r != nil { +// err = errors.Errorf("failure: %s", r) +// } +// }() + +// rand, err := i.curve.Rand() +// if err != nil { +// return nil, errors.Errorf("failed to obtain randomness source: %v", err) +// } + +// return i.idemix.CreateCRI(key, unrevokedHandles, epoch, alg, rand, i.idemix.Translator) +// } + +// func (i *libImpl) GenerateLongTermRevocationKey() (pk *ecdsa.PrivateKey, err error) { +// defer func() { +// r := recover() +// if r != nil { +// err = errors.Errorf("failure: %s", r) +// } +// }() +// return scheme.GenerateLongTermRevocationKey() +// } diff --git a/lib/server/idemix/idemixlib_test.go b/lib/server/idemix/idemixlib_test.go deleted file mode 100644 index dfb6d9828..000000000 --- a/lib/server/idemix/idemixlib_test.go +++ /dev/null @@ -1,87 +0,0 @@ -/* -Copyright IBM Corp. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package idemix_test - -import ( - "fmt" - "os" - "path/filepath" - "strings" - "testing" - - math "github.com/IBM/mathlib" - "github.com/golang/protobuf/proto" - cidemix "github.com/hyperledger/fabric-ca/lib/common/idemix" - "github.com/hyperledger/fabric-ca/lib/server/idemix" - "github.com/hyperledger/fabric-ca/util" - "github.com/stretchr/testify/assert" -) - -func TestIdemixPanic(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testIdemixPanic(t, curve) - }) - } -} - -func generatePublicPrivateKeyPair(t *testing.T, curveID cidemix.CurveID) (string, string, string, error) { - tmpDir, err := os.MkdirTemp(os.TempDir(), strings.Replace(t.Name(), "/", "-", -1)) - assert.NoError(t, err) - - testPublicKeyFile := filepath.Join(tmpDir, "IdemixPublicKey") - testSecretKeyFile := filepath.Join(tmpDir, "IdemixSecretKey") - - pk, sk := makePubPrivKeyPair(curveID, t) - err = os.WriteFile(testPublicKeyFile, pk, 0o644) - if err != nil { - t.Fatalf("Failed writing public key to file: %s", err.Error()) - } - - err = os.WriteFile(testSecretKeyFile, sk, 0o644) - if err != nil { - t.Fatalf("Failed writing private key to file: %s", err.Error()) - } - return testPublicKeyFile, testSecretKeyFile, tmpDir, err -} - -func makePubPrivKeyPair(curveID cidemix.CurveID, t *testing.T) ([]byte, []byte) { - curve := cidemix.CurveByID(curveID) - rand, err := curve.Rand() - assert.NoError(t, err) - - attrs := []string{idemix.AttrOU, idemix.AttrRole, idemix.AttrEnrollmentID, idemix.AttrRevocationHandle} - var numericalAttrs []*math.Zr - for _, attr := range attrs { - numericalAttrs = append(numericalAttrs, curve.HashToZr([]byte(attr))) - } - - idemix := cidemix.InstanceForCurve(curveID) - ik, err := idemix.NewIssuerKey(attrs, rand, idemix.Translator) - assert.NoError(t, err) - - ipk, err := proto.Marshal(ik.Ipk) - assert.NoError(t, err) - - return ipk, ik.Isk -} - -func testIdemixPanic(t *testing.T, curveID cidemix.CurveID) { - testPublicKeyFile, testSecretKeyFile, tmpDir, err := generatePublicPrivateKeyPair(t, curveID) - defer os.RemoveAll(tmpDir) - - curve := cidemix.CurveByID(curveID) - nonce := curve.NewZrFromInt(1) - credReq, _, err := newIdemixCredentialRequest(t, nonce, curveID, testPublicKeyFile, testSecretKeyFile) - if err != nil { - t.Fatalf("Failed to create credential request: %s", err.Error()) - } - - libImpl := idemix.NewLib(curveID) - _, err = libImpl.NewCredential(nil, credReq, nil) - util.ErrorContains(t, err, "failure: runtime error", "NewCredential should have caught panic, and returned an error") -} diff --git a/lib/server/idemix/issuer.go b/lib/server/idemix/issuer.go index 08b8d731c..d29d485ea 100644 --- a/lib/server/idemix/issuer.go +++ b/lib/server/idemix/issuer.go @@ -7,26 +7,20 @@ SPDX-License-Identifier: Apache-2.0 package idemix import ( - "crypto/x509" - "encoding/pem" "fmt" "reflect" "strings" "sync" "time" - schemes "github.com/IBM/idemix/bccsp/schemes" - idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" - math "github.com/IBM/mathlib" + bccsp "github.com/IBM/idemix/bccsp/types" "github.com/cloudflare/cfssl/log" - proto "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric-ca/api" - cidemix "github.com/hyperledger/fabric-ca/lib/common/idemix" "github.com/hyperledger/fabric-ca/lib/server/db" dbutil "github.com/hyperledger/fabric-ca/lib/server/db/util" "github.com/hyperledger/fabric-ca/lib/server/user" "github.com/hyperledger/fabric-ca/util" - "github.com/hyperledger/fabric/bccsp" + fabric_bccsp "github.com/hyperledger/fabric/bccsp" "github.com/pkg/errors" ) @@ -40,23 +34,13 @@ type Issuer interface { VerifyToken(authHdr, method, uri string, body []byte) (string, error) } -//go:generate mockery --name MyIssuer --case underscore - -// MyIssuer provides functions for accessing issuer components -type MyIssuer interface { - Name() string - HomeDir() string - Config() *Config - IdemixLib() Lib - DB() db.FabricCADB - IssuerCredential() IssuerCredential - RevocationAuthority() RevocationAuthority - NonceManager() NonceManager - CredDBAccessor() CredDBAccessor -} - //go:generate mockery --name ServerRequestCtx --case underscore +//go:generate mockery --name UserUser --case underscore +type UserUser interface { + user.User +} + // ServerRequestCtx is the server request context that Idemix enroll expects type ServerRequestCtx interface { IsBasicAuth() bool @@ -66,34 +50,30 @@ type ServerRequestCtx interface { ReadBody(body interface{}) error } -type issuer struct { - name string - homeDir string - cfg *Config - idemixLib Lib - db db.FabricCADB - csp bccsp.BCCSP +type IssuerInst struct { + Name string + HomeDir string + Cfg *Config + Db db.FabricCADB + Csp bccsp.BCCSP // The Idemix credential DB accessor - credDBAccessor CredDBAccessor + CredDBAccessor CredDBAccessor // idemix issuer credential for the CA - issuerCred IssuerCredential - rc RevocationAuthority - nm NonceManager - isInitialized bool - mutex sync.Mutex - curveID cidemix.CurveID - t idemix.Translator - curve *math.Curve + IssuerCred IssuerCredential + RevocationAuthority RevocationAuthority + NonceManager NonceManager + IsInitialized bool + mutex sync.Mutex } // NewIssuer returns an object that implements Issuer interface -func NewIssuer(name, homeDir string, config *Config, csp bccsp.BCCSP, idemixLib Lib, curveID cidemix.CurveID) Issuer { - issuer := issuer{name: name, homeDir: homeDir, cfg: config, csp: csp, idemixLib: idemixLib, curveID: curveID, curve: cidemix.CurveByID(curveID), t: cidemix.InstanceForCurve(curveID).Translator} +func NewIssuer(name, homeDir string, config *Config, csp bccsp.BCCSP) Issuer { + issuer := IssuerInst{Name: name, HomeDir: homeDir, Cfg: config, Csp: csp} return &issuer } -func (i *issuer) Init(renew bool, db db.FabricCADB, levels *dbutil.Levels) error { - if i.isInitialized { +func (i *IssuerInst) Init(renew bool, db db.FabricCADB, levels *dbutil.Levels) error { + if i.IsInitialized { return nil } @@ -101,16 +81,16 @@ func (i *issuer) Init(renew bool, db db.FabricCADB, levels *dbutil.Levels) error defer i.mutex.Unlock() // After obtaining a lock, check again to see if issuer has been initialized by another thread - if i.isInitialized { + if i.IsInitialized { return nil } if db == nil || reflect.ValueOf(db).IsNil() || !db.IsInitialized() { - log.Debugf("Returning without initializing Idemix issuer for CA '%s' as the database is not initialized", i.Name()) + log.Debugf("Returning without initializing Idemix issuer for CA '%s' as the database is not initialized", i.Name) return nil } - i.db = db - err := i.cfg.init(i.homeDir) + i.Db = db + err := i.Cfg.init(i.HomeDir) if err != nil { return err } @@ -118,66 +98,65 @@ func (i *issuer) Init(renew bool, db db.FabricCADB, levels *dbutil.Levels) error if err != nil { return err } - i.credDBAccessor = NewCredentialAccessor(i.db, levels.Credential) - log.Debugf("Intializing revocation authority for issuer '%s'", i.Name()) - i.rc, err = NewRevocationAuthority(i, levels.RAInfo, i.curveID) + i.CredDBAccessor = NewCredentialAccessor(i.Db, levels.Credential) + log.Debugf("Intializing revocation authority for issuer '%s'", i.Name) + i.RevocationAuthority, err = NewRevocationAuthority(i, levels.RAInfo) if err != nil { return err } - log.Debugf("Intializing nonce manager for issuer '%s'", i.Name()) - i.nm, err = NewNonceManager(i, &wallClock{}, levels.Nonce) + log.Debugf("Intializing nonce manager for issuer '%s'", i.Name) + i.NonceManager, err = NewNonceManager(i, &wallClock{}, levels.Nonce) if err != nil { return err } - i.isInitialized = true + i.IsInitialized = true return nil } -func (i *issuer) IssuerPublicKey() ([]byte, error) { - if !i.isInitialized { +func (i *IssuerInst) IssuerPublicKey() ([]byte, error) { + if !i.IsInitialized { return nil, errors.New("Issuer is not initialized") } - ik, err := i.issuerCred.GetIssuerKey() + isk, err := i.IssuerCred.GetIssuerKey() if err != nil { return nil, err } - ipkBytes, err := proto.Marshal(ik.Ipk) + + ipk, err := isk.PublicKey() + if err != nil { + return nil, err + } + + ipkBytes, err := ipk.Bytes() if err != nil { return nil, err } return ipkBytes, nil } -func (i *issuer) RevocationPublicKey() ([]byte, error) { - if !i.isInitialized { +func (i *IssuerInst) RevocationPublicKey() ([]byte, error) { + if !i.IsInitialized { return nil, errors.New("Issuer is not initialized") } - rpk := i.RevocationAuthority().PublicKey() - encodedPubKey, err := x509.MarshalPKIXPublicKey(rpk) - if err != nil { - return nil, errors.Wrapf(err, "Failed to encode revocation authority public key of the issuer %s", i.Name()) - } - pemEncodedPubKey := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: encodedPubKey}) - return pemEncodedPubKey, nil + rpk := i.RevocationAuthority.PublicKey() + + return rpk.Bytes() } -func (i *issuer) IssueCredential(ctx ServerRequestCtx) (*EnrollmentResponse, error) { - if !i.isInitialized { +func (i *IssuerInst) IssueCredential(ctx ServerRequestCtx) (*EnrollmentResponse, error) { + if !i.IsInitialized { return nil, errors.New("Issuer is not initialized") } handler := EnrollRequestHandler{ - Curve: i.curve, - CurveID: i.curveID, - Ctx: ctx, - Issuer: i, - IdmxLib: i.idemixLib, + Ctx: ctx, + Issuer: i, } return handler.HandleRequest() } -func (i *issuer) GetCRI(ctx ServerRequestCtx) (*api.GetCRIResponse, error) { - if !i.isInitialized { +func (i *IssuerInst) GetCRI(ctx ServerRequestCtx) (*api.GetCRIResponse, error) { + if !i.IsInitialized { return nil, errors.New("Issuer is not initialized") } handler := CRIRequestHandler{ @@ -188,8 +167,8 @@ func (i *issuer) GetCRI(ctx ServerRequestCtx) (*api.GetCRIResponse, error) { return handler.HandleRequest() } -func (i *issuer) VerifyToken(authHdr, method, uri string, body []byte) (string, error) { - if !i.isInitialized { +func (i *IssuerInst) VerifyToken(authHdr, method, uri string, body []byte) (string, error) { + if !i.IsInitialized { return "", errors.New("Issuer is not initialized") } // Disclosure array indicates which attributes are disclosed. 1 means disclosed. Currently four attributes are @@ -198,7 +177,6 @@ func (i *issuer) VerifyToken(authHdr, method, uri string, body []byte) (string, // EnrollmentID is disclosed to check if the signature was infact created using credential of a user whose // enrollment ID is the one specified in the token. So, enrollment ID in the token is used to check if the user // is valid and has a credential (by checking the DB) and it is used to verify zero knowledge proof. - disclosure := []byte{0, 0, 1, 0} parts := getTokenParts(authHdr) if parts == nil { return "", errors.New("Invalid Idemix token format; token format must be: 'idemix..'") @@ -207,28 +185,32 @@ func (i *issuer) VerifyToken(authHdr, method, uri string, body []byte) (string, return "", errors.New("Invalid version found in the Idemix token. Version must be 1") } enrollmentID := parts[2] - creds, err := i.credDBAccessor.GetCredentialsByID(enrollmentID) + creds, err := i.CredDBAccessor.GetCredentialsByID(enrollmentID) if err != nil { return "", errors.Errorf("Failed to check if enrollment ID '%s' is valid", enrollmentID) } if len(creds) == 0 { return "", errors.Errorf("Enrollment ID '%s' does not have any Idemix credentials", enrollmentID) } - idBytes := []byte(enrollmentID) - attrs := []*math.Zr{nil, nil, i.curve.HashToZr(idBytes), nil} b64body := util.B64Encode(body) b64uri := util.B64Encode([]byte(uri)) msg := method + "." + b64uri + "." + b64body - digest, digestError := i.csp.Hash([]byte(msg), &bccsp.SHAOpts{}) + digest, digestError := i.Csp.Hash([]byte(msg), &fabric_bccsp.SHAOpts{}) if digestError != nil { return "", errors.WithMessage(digestError, fmt.Sprintf("Failed to create authentication token '%s'", msg)) } - issuerKey, err := i.issuerCred.GetIssuerKey() + issuerSecretKey, err := i.IssuerCred.GetIssuerKey() if err != nil { return "", errors.WithMessage(err, "Failed to get issuer key") } - ra := i.RevocationAuthority() + + IssuerPublicKey, err := issuerSecretKey.PublicKey() + if err != nil { + return "", errors.WithMessage(err, "Failed to get issuer public key") + } + + ra := i.RevocationAuthority epoch, err := ra.Epoch() if err != nil { return "", err @@ -238,68 +220,35 @@ func (i *issuer) VerifyToken(authHdr, method, uri string, body []byte) (string, if err != nil { return "", errors.WithMessage(err, "Failed to base64 decode signature specified in the token") } - sig := &idemix.Signature{} - err = proto.Unmarshal(sigBytes, sig) - if err != nil { - return "", errors.WithMessage(err, "Failed to unmarshal signature bytes specified in the token") - } - err = sig.Ver(disclosure, issuerKey.Ipk, digest, attrs, 3, 0, ra.PublicKey(), epoch, i.curve, i.t, schemes.BestEffort, nil) - if err != nil { + + valid, err := i.Csp.Verify( + IssuerPublicKey, + sigBytes, + digest, + &bccsp.IdemixSignerOpts{ + Attributes: []bccsp.IdemixAttribute{ + {Type: bccsp.IdemixHiddenAttribute}, + {Type: bccsp.IdemixHiddenAttribute}, + {Type: bccsp.IdemixBytesAttribute, Value: []byte(enrollmentID)}, + {Type: bccsp.IdemixHiddenAttribute}, + }, + RhIndex: 3, + EidIndex: 2, + VerificationType: bccsp.BestEffort, + Epoch: epoch, + }, + ) + if err != nil || !valid { return "", errors.WithMessage(err, "Failed to verify the token") } - return enrollmentID, nil -} - -// Name returns the name of the issuer -func (i *issuer) Name() string { - return i.name -} - -// HomeDir returns the home directory of the issuer -func (i *issuer) HomeDir() string { - return i.homeDir -} - -// Config returns config of this issuer -func (i *issuer) Config() *Config { - return i.cfg -} - -// IdemixLib return idemix library instance -func (i *issuer) IdemixLib() Lib { - return i.idemixLib -} - -// DB returns the FabricCADB object (which represents database handle -// to the CA database) associated with this issuer -func (i *issuer) DB() db.FabricCADB { - return i.db -} - -// IssuerCredential returns IssuerCredential of this issuer -func (i *issuer) IssuerCredential() IssuerCredential { - return i.issuerCred -} -// RevocationAuthority returns revocation authority of this issuer -func (i *issuer) RevocationAuthority() RevocationAuthority { - return i.rc -} - -// NonceManager returns nonce manager of this issuer -func (i *issuer) NonceManager() NonceManager { - return i.nm -} - -// CredDBAccessor returns the Idemix credential DB accessor for issuer -func (i *issuer) CredDBAccessor() CredDBAccessor { - return i.credDBAccessor + return enrollmentID, nil } -func (i *issuer) initKeyMaterial(renew bool) error { - idemixPubKey := i.cfg.IssuerPublicKeyfile - idemixSecretKey := i.cfg.IssuerSecretKeyfile - issuerCred := NewIssuerCredential(idemixPubKey, idemixSecretKey, i.idemixLib, i.curveID) +func (i *IssuerInst) initKeyMaterial(renew bool) error { + idemixPubKey := i.Cfg.IssuerPublicKeyfile + idemixSecretKey := i.Cfg.IssuerSecretKeyfile + issuerCred := NewIssuerCredential(idemixPubKey, idemixSecretKey, i.Csp) log.Debugf("renew is set to [%v]", renew) if !renew { @@ -315,7 +264,7 @@ func (i *issuer) initKeyMaterial(renew bool) error { if err != nil { return err } - i.issuerCred = issuerCred + i.IssuerCred = issuerCred return nil } } @@ -323,13 +272,13 @@ func (i *issuer) initKeyMaterial(renew bool) error { if err != nil { return err } - log.Debugf("Idemix issuer public and secret keys were generated for CA '%s'", i.name) + log.Debugf("Idemix issuer public and secret keys were generated for CA '%s'", i.Name) issuerCred.SetIssuerKey(ik) err = issuerCred.Store() if err != nil { return err } - i.issuerCred = issuerCred + i.IssuerCred = issuerCred return nil } diff --git a/lib/server/idemix/issuer_test.go b/lib/server/idemix/issuer_test.go index 33c22f710..3446a4f84 100644 --- a/lib/server/idemix/issuer_test.go +++ b/lib/server/idemix/issuer_test.go @@ -7,16 +7,12 @@ SPDX-License-Identifier: Apache-2.0 package idemix_test import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" "fmt" "os" "path/filepath" "testing" "github.com/hyperledger/fabric-ca/lib" - cidemix "github.com/hyperledger/fabric-ca/lib/common/idemix" dbutil "github.com/hyperledger/fabric-ca/lib/server/db/util" . "github.com/hyperledger/fabric-ca/lib/server/idemix" "github.com/hyperledger/fabric-ca/lib/server/idemix/mocks" @@ -29,39 +25,22 @@ import ( ) func TestNewIssuer(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testNewIssuer(t, curve) - }) - } -} - -func testNewIssuer(t *testing.T, curveID cidemix.CurveID) { - lib := new(mocks.Lib) cfg := &Config{ NonceExpiration: "15", NonceSweepInterval: "15", } - issuer := NewIssuer("ca1", ".", cfg, util.GetDefaultBCCSP(), lib, curveID) + issuer := NewIssuer("ca1", ".", cfg, getCSP(t)) assert.NotNil(t, issuer) } func TestInit(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testInit(t, curve) - }) - } -} - -func testInit(t *testing.T, curveID cidemix.CurveID) { testdir := t.TempDir() err := os.MkdirAll(filepath.Join(testdir, "msp/keystore"), 0o777) if err != nil { t.Fatalf("Failed to create directory: %s", err.Error()) } - db, issuer := getIssuer(t, testdir, false, false, curveID) + db, issuer := getIssuer(t, testdir, false, false) assert.NotNil(t, issuer) err = issuer.Init(false, nil, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) assert.NoError(t, err, "Init should not return an error if db is nil") @@ -83,40 +62,24 @@ func testInit(t *testing.T, curveID cidemix.CurveID) { } func TestInitDBNotInitialized(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testInitDBNotInitialized(t, curve) - }) - } -} - -func testInitDBNotInitialized(t *testing.T, curveID cidemix.CurveID) { cfg := &Config{ NonceExpiration: "15s", NonceSweepInterval: "15m", } - var db *dmocks.FabricCADB - issuer := NewIssuer("ca1", ".", cfg, util.GetDefaultBCCSP(), NewLib(curveID), curveID) + var db *dmocks.DbFabricCADB + issuer := NewIssuer("ca1", ".", cfg, getCSP(t)) err := issuer.Init(false, db, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) assert.NoError(t, err) - db = new(dmocks.FabricCADB) + db = new(dmocks.DbFabricCADB) db.On("IsInitialized").Return(false) - issuer = NewIssuer("ca1", ".", cfg, util.GetDefaultBCCSP(), NewLib(curveID), curveID) + issuer = NewIssuer("ca1", ".", cfg, getCSP(t)) err = issuer.Init(false, db, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) assert.NoError(t, err) } func TestInitExistingIssuerCredential(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testInitExistingIssuerCredential(t, curve) - }) - } -} - -func testInitExistingIssuerCredential(t *testing.T, curveID cidemix.CurveID) { - testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) @@ -134,7 +97,7 @@ func testInitExistingIssuerCredential(t *testing.T, curveID cidemix.CurveID) { t.Fatalf("Failed to copy file: %s", err.Error()) } - db, issuer := getIssuer(t, testdir, false, false, curveID) + db, issuer := getIssuer(t, testdir, false, false) assert.NotNil(t, issuer) secrekeyfile := filepath.Join(testdir, "msp/keystore/IssuerSecretKey") @@ -159,24 +122,16 @@ func testInitExistingIssuerCredential(t *testing.T, curveID cidemix.CurveID) { } func TestInitRenewTrue(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testInitRenewTrue(t, curve) - }) - } -} - -func testInitRenewTrue(t *testing.T, curveID cidemix.CurveID) { testdir := t.TempDir() - db, issuer := getIssuer(t, testdir, true, false, curveID) + db, issuer := getIssuer(t, testdir, true, false) assert.NotNil(t, issuer) - db, issuer = getIssuer(t, testdir, false, true, curveID) + db, issuer = getIssuer(t, testdir, false, true) assert.NotNil(t, issuer) err := issuer.Init(true, db, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) assert.Error(t, err, "Init should fail if it fails to create new issuer key") - db, issuer = getIssuer(t, testdir, false, false, curveID) + db, issuer = getIssuer(t, testdir, false, false) assert.NotNil(t, issuer) testdataInfo, err := os.Stat(testdir) @@ -199,15 +154,7 @@ func testInitRenewTrue(t *testing.T, curveID cidemix.CurveID) { } func TestVerifyTokenError(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testVerifyTokenError(t, curve) - }) - } -} - -func testVerifyTokenError(t *testing.T, curveID cidemix.CurveID) { - testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) @@ -226,7 +173,7 @@ func testVerifyTokenError(t *testing.T, curveID cidemix.CurveID) { t.Fatalf("Failed to copy file: %s", err.Error()) } - db, issuer := getIssuer(t, testdir, false, false, curveID) + db, issuer := getIssuer(t, testdir, false, false) assert.NotNil(t, issuer) _, err = issuer.VerifyToken("idemix.1.foo.blah", "", "", []byte{}) @@ -251,15 +198,7 @@ func testVerifyTokenError(t *testing.T, curveID cidemix.CurveID) { } func TestVerifyTokenNoCreds(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testVerifyTokenNoCreds(t, curve) - }) - } -} - -func testVerifyTokenNoCreds(t *testing.T, curveID cidemix.CurveID) { - testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) @@ -277,7 +216,7 @@ func testVerifyTokenNoCreds(t *testing.T, curveID cidemix.CurveID) { t.Fatalf("Failed to copy file: %s", err.Error()) } - db, issuer := getIssuer(t, testdir, false, false, curveID) + db, issuer := getIssuer(t, testdir, false, false) assert.NotNil(t, issuer) err = issuer.Init(false, db, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) @@ -294,15 +233,7 @@ func testVerifyTokenNoCreds(t *testing.T, curveID cidemix.CurveID) { } func TestVerifyTokenBadSignatureEncoding(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testVerifyTokenBadSignatureEncoding(t, curve) - }) - } -} - -func testVerifyTokenBadSignatureEncoding(t *testing.T, curveID cidemix.CurveID) { - testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) @@ -320,7 +251,7 @@ func testVerifyTokenBadSignatureEncoding(t *testing.T, curveID cidemix.CurveID) t.Fatalf("Failed to copy file: %s", err.Error()) } - db, issuer := getIssuer(t, testdir, false, false, curveID) + db, issuer := getIssuer(t, testdir, false, false) assert.NotNil(t, issuer) err = issuer.Init(false, db, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) @@ -338,15 +269,7 @@ func testVerifyTokenBadSignatureEncoding(t *testing.T, curveID cidemix.CurveID) } func TestVerifyTokenBadSignature(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testVerifyTokenBadSignature(t, curve) - }) - } -} - -func testVerifyTokenBadSignature(t *testing.T, curveID cidemix.CurveID) { - testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) @@ -364,7 +287,7 @@ func testVerifyTokenBadSignature(t *testing.T, curveID cidemix.CurveID) { t.Fatalf("Failed to copy file: %s", err.Error()) } - db, issuer := getIssuer(t, testdir, false, false, curveID) + db, issuer := getIssuer(t, testdir, false, false) assert.NotNil(t, issuer) err = issuer.Init(false, db, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) @@ -398,15 +321,7 @@ func TestIsToken(t *testing.T) { } func TestRevocationPublicKey(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testRevocationPublicKey(t, curve) - }) - } -} - -func testRevocationPublicKey(t *testing.T, curveID cidemix.CurveID) { - testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) @@ -424,7 +339,7 @@ func testRevocationPublicKey(t *testing.T, curveID cidemix.CurveID) { t.Fatalf("Failed to copy file: %s", err.Error()) } - db, issuer := getIssuer(t, testdir, false, false, curveID) + db, issuer := getIssuer(t, testdir, false, false) assert.NotNil(t, issuer) err = issuer.Init(false, db, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) @@ -433,95 +348,3 @@ func testRevocationPublicKey(t *testing.T, curveID cidemix.CurveID) { _, err = issuer.RevocationPublicKey() assert.NoError(t, err, "RevocationPublicKey should not return an error") } - -func getIssuer(t *testing.T, testDir string, getranderror, newIssuerKeyerror bool, curveID cidemix.CurveID) (*dmocks.FabricCADB, Issuer) { - err := os.MkdirAll(filepath.Join(testDir, "msp/keystore"), 0o777) - if err != nil { - t.Fatalf("Failed to create directory: %s", err.Error()) - } - - db := new(dmocks.FabricCADB) - - tx := new(dmocks.FabricCATx) - tx.On("Commit").Return(nil) - tx.On("Rollback").Return(nil) - tx.On("Rebind", SelectRAInfo).Return(SelectRAInfo) - tx.On("Rebind", UpdateNextHandle).Return(UpdateNextHandle) - tx.On("Exec", UpdateNextHandle, 2, 1).Return(nil, nil) - rcInfos := []RevocationAuthorityInfo{} - f1 := getTxSelectFunc(t, &rcInfos, 1, false, true) - tx.On("Select", &rcInfos, SelectRAInfo).Return(f1) - - db.On("BeginTx").Return(tx) - db.On("IsInitialized").Return(true) - - lib := new(mocks.Lib) - - idemix := cidemix.InstanceForCurve(curveID) - curve := cidemix.CurveByID(curveID) - rnd, err := curve.Rand() - if err != nil { - t.Fatalf("Failed to get random number: %s", err.Error()) - } - ik, err := idemix.NewIssuerKey(GetAttributeNames(), rnd, idemix.Translator) - if err != nil { - t.Fatalf("Failed to generate issuer key: %s", err.Error()) - } - if getranderror { - lib.On("GetRand").Return(nil, errors.New("Failed to generate random number")) - } else { - lib.On("GetRand").Return(rnd, nil) - } - - if newIssuerKeyerror { - lib.On("NewIssuerKey", GetAttributeNames()).Return(nil, errors.New("Failed to generate new issuer key")) - } else { - lib.On("NewIssuerKey", GetAttributeNames()).Return(ik, nil) - } - - key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) - if err != nil { - t.Fatalf("Failed to generate key: %s", err.Error()) - } - lib.On("GenerateLongTermRevocationKey").Return(key, nil) - - cfg := &Config{ - RHPoolSize: 100, - NonceExpiration: "15s", - NonceSweepInterval: "15m", - } - issuer := NewIssuer("ca1", testDir, cfg, util.GetDefaultBCCSP(), lib, curveID) - - f := getSelectFunc(t, true, false) - - rcInfosForSelect := []RevocationAuthorityInfo{} - db.On("Select", "GetRAInfo", &rcInfosForSelect, SelectRAInfo).Return(f) - rcinfo := RevocationAuthorityInfo{ - Epoch: 1, - NextRevocationHandle: 1, - LastHandleInPool: 100, - Level: 1, - } - result := new(dmocks.Result) - result.On("RowsAffected").Return(int64(1), nil) - db.On("NamedExec", "AddRAInfo", InsertRAInfo, &rcinfo).Return(result, nil) - - return db, issuer -} - -func getCredsSelectFunc(t *testing.T, creds *[]CredRecord, isAppend bool) func(string, interface{}, string, ...interface{}) error { - return func(funcName string, dest interface{}, query string, args ...interface{}) error { - credRecs := dest.(*[]CredRecord) - cred := CredRecord{ - ID: "foo", - Status: "active", - Cred: "", - } - - if isAppend { - //*creds = append(*creds, cred) - *credRecs = append(*credRecs, cred) - } - return nil - } -} diff --git a/lib/server/idemix/issuer_whitebox_test.go b/lib/server/idemix/issuer_whitebox_test.go index 36ca5126f..b22dd5f9a 100644 --- a/lib/server/idemix/issuer_whitebox_test.go +++ b/lib/server/idemix/issuer_whitebox_test.go @@ -4,47 +4,35 @@ Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package idemix +package idemix_test import ( - "fmt" + "errors" "os" "path/filepath" - "strings" "testing" - math "github.com/IBM/mathlib" - "github.com/golang/protobuf/proto" - idemix2 "github.com/hyperledger/fabric-ca/lib/common/idemix" + "github.com/IBM/idemix/bccsp/types" "github.com/hyperledger/fabric-ca/lib/server/db" dbutil "github.com/hyperledger/fabric-ca/lib/server/db/util" + . "github.com/hyperledger/fabric-ca/lib/server/idemix" + "github.com/hyperledger/fabric-ca/lib/server/idemix/mocks" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) // TestIssuer tests issuer func TestIssuer(t *testing.T) { - for _, curve := range idemix2.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testIssuer(t, curve) - }) - } -} - -// TestIssuer tests issuer -func testIssuer(t *testing.T, curveID idemix2.CurveID) { testdir := t.TempDir() err := os.MkdirAll(filepath.Join(testdir, "msp/keystore"), 0o777) if err != nil { t.Fatalf("Failed to create directory: %s", err.Error()) } - issuer := issuer{name: "ca1", homeDir: testdir, cfg: &Config{}, db: &db.DB{}, idemixLib: NewLib(curveID)} - assert.NotNil(t, issuer.DB(), "DB() should not return nil") - assert.NotNil(t, issuer.IdemixLib(), "GetIdemixLib() should not return nil") - assert.Equal(t, "ca1", issuer.Name()) - assert.Nil(t, issuer.IssuerCredential(), "IssueCredential() should return nil") - assert.Nil(t, issuer.RevocationAuthority(), "RevocationAuthority() should return nil") - assert.Nil(t, issuer.NonceManager(), "NonceManager() should return nil") - assert.Nil(t, issuer.CredDBAccessor(), "CredDBAccessor() should return nil") + issuer := &IssuerInst{Name: "ca1", HomeDir: testdir, Cfg: &Config{}, Db: &db.DB{}} + assert.Nil(t, issuer.IssuerCred, "IssueCredential() should return nil") + assert.Nil(t, issuer.RevocationAuthority, "RevocationAuthority() should return nil") + assert.Nil(t, issuer.NonceManager, "NonceManager() should return nil") + assert.Nil(t, issuer.CredDBAccessor, "CredDBAccessor() should return nil") err = issuer.Init(false, nil, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) assert.NoError(t, err, "Init should return not return an error if db is nil") @@ -65,21 +53,13 @@ func testIssuer(t *testing.T, curveID idemix2.CurveID) { assert.Error(t, err, "GetCRI should return an error because issuer is not initialized") assert.Equal(t, "Issuer is not initialized", err.Error()) - issuer.isInitialized = true + issuer.IsInitialized = true err = issuer.Init(false, nil, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) assert.NoError(t, err, "Init should return not return an error if it is already initialized") } func TestIssuerPublicKey(t *testing.T) { - for _, curve := range idemix2.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testIssuerPublicKey(t, curve) - }) - } -} - -func testIssuerPublicKey(t *testing.T, curveID idemix2.CurveID) { - testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) @@ -91,71 +71,33 @@ func testIssuerPublicKey(t *testing.T, curveID idemix2.CurveID) { t.Fatalf("Failed to create directory: %s", err.Error()) } - issuer := issuer{ - name: "ca1", - homeDir: testdir, - cfg: &Config{IssuerPublicKeyfile: "IssuerPublicKey", IssuerSecretKeyfile: "IssuerSecretKey"}, - db: &db.DB{}, - idemixLib: NewLib(curveID), - isInitialized: true, + issuer := &IssuerInst{ + Name: "ca1", + HomeDir: testdir, + Cfg: &Config{IssuerPublicKeyfile: "IssuerPublicKey", IssuerSecretKeyfile: "IssuerSecretKey"}, + Db: &db.DB{}, + IsInitialized: true, } - issuerCred := NewIssuerCredential(testPublicKeyFile, testSecretKeyFile, NewLib(curveID), curveID) - issuer.issuerCred = issuerCred + + isk := new(mocks.BccspKey) + ipk := new(mocks.BccspKey) + isk.On("PublicKey").Return(ipk, nil) + isk.On("Bytes").Return([]byte("isk_Bytes"), nil) + ipk.On("Bytes").Return(nil, errors.New("barf")) + + mockCsp := new(mocks.BccspBCCSP) + mockCsp.On("KeyImport", mock.Anything, &types.IdemixIssuerPublicKeyImportOpts{Temporary: true, AttributeNames: []string{"OU", "Role", "EnrollmentID", "RevocationHandle"}}).Return(ipk, nil) + mockCsp.On("KeyImport", mock.Anything, &types.IdemixIssuerKeyImportOpts{Temporary: true, AttributeNames: []string{"OU", "Role", "EnrollmentID", "RevocationHandle"}}).Return(isk, nil) + + issuerCred := NewIssuerCredential(testPublicKeyFile, testSecretKeyFile, mockCsp) + issuer.IssuerCred = issuerCred _, err = issuer.IssuerPublicKey() assert.Error(t, err, "issuer.IssuerCredential() should return an error as issuer credential has not been loaded") - err = issuer.issuerCred.Load() + err = issuer.IssuerCred.Load() if err != nil { t.Fatalf("Failed to load issuer credential: %s", err.Error()) } - ik, _ := issuerCred.GetIssuerKey() - ik.Ipk = nil _, err = issuer.IssuerPublicKey() assert.Error(t, err, "issuer.IssuerCredential() should return an error as it should fail to marshal issuer public key") } - -func GeneratePublicPrivateKeyPair(t *testing.T, curveID idemix2.CurveID) (string, string, string, error) { - tmpDir, err := os.MkdirTemp(os.TempDir(), strings.Replace(t.Name(), "/", "-", -1)) - assert.NoError(t, err) - - testPublicKeyFile := filepath.Join(tmpDir, "IdemixPublicKey") - testSecretKeyFile := filepath.Join(tmpDir, "IdemixSecretKey") - - pk, sk := makePubPrivKeyPair(curveID, t) - err = os.WriteFile(testPublicKeyFile, pk, 0o644) - if err != nil { - t.Fatalf("Failed writing public key to file: %s", err.Error()) - } - - err = os.WriteFile(testSecretKeyFile, sk, 0o644) - if err != nil { - t.Fatalf("Failed writing private key to file: %s", err.Error()) - } - return testPublicKeyFile, testSecretKeyFile, tmpDir, err -} - -func TestWallClock(t *testing.T) { - clock := wallClock{} - assert.NotNil(t, clock.Now()) -} - -func makePubPrivKeyPair(curveID idemix2.CurveID, t *testing.T) ([]byte, []byte) { - curve := idemix2.CurveByID(curveID) - rand, err := curve.Rand() - assert.NoError(t, err) - - attrs := []string{AttrOU, AttrRole, AttrEnrollmentID, AttrRevocationHandle} - var numericalAttrs []*math.Zr - for _, attr := range attrs { - numericalAttrs = append(numericalAttrs, curve.HashToZr([]byte(attr))) - } - - idemix := idemix2.InstanceForCurve(curveID) - ik, err := idemix.NewIssuerKey(attrs, rand, idemix.Translator) - assert.NoError(t, err) - - ipk, err := proto.Marshal(ik.Ipk) - assert.NoError(t, err) - - return ipk, ik.Isk -} diff --git a/lib/server/idemix/issuercredential.go b/lib/server/idemix/issuercredential.go index 4a794690c..e6cd9142a 100644 --- a/lib/server/idemix/issuercredential.go +++ b/lib/server/idemix/issuercredential.go @@ -9,11 +9,8 @@ package idemix import ( "os" - idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" - math "github.com/IBM/mathlib" + bccsp "github.com/IBM/idemix/bccsp/types" "github.com/cloudflare/cfssl/log" - proto "github.com/golang/protobuf/proto" - cidemix "github.com/hyperledger/fabric-ca/lib/common/idemix" "github.com/hyperledger/fabric-ca/util" "github.com/pkg/errors" ) @@ -36,32 +33,28 @@ type IssuerCredential interface { // Store stores the CA's Idemix credential to the disk Store() error // GetIssuerKey returns *idemix.IssuerKey that represents - // CA's Idemix public and secret key - GetIssuerKey() (*idemix.IssuerKey, error) + // CA's Idemix secret key + GetIssuerKey() (bccsp.Key, error) // SetIssuerKey sets issuer key - SetIssuerKey(key *idemix.IssuerKey) + SetIssuerKey(bccsp.Key) // Returns new instance of idemix.IssuerKey - NewIssuerKey() (*idemix.IssuerKey, error) + NewIssuerKey() (bccsp.Key, error) } // caIdemixCredential implements IssuerCredential interface type caIdemixCredential struct { pubKeyFile string secretKeyFile string - issuerKey *idemix.IssuerKey - idemixLib Lib - curve *math.Curve - t idemix.Translator + issuerKey bccsp.Key + CSP bccsp.BCCSP } // NewIssuerCredential returns an instance of an object that implements IssuerCredential interface -func NewIssuerCredential(pubKeyFile, secretKeyFile string, lib Lib, curveID cidemix.CurveID) IssuerCredential { +func NewIssuerCredential(pubKeyFile, secretKeyFile string, CSP bccsp.BCCSP) IssuerCredential { return &caIdemixCredential{ pubKeyFile: pubKeyFile, secretKeyFile: secretKeyFile, - idemixLib: lib, - curve: cidemix.CurveByID(curveID), - t: cidemix.InstanceForCurve(curveID).Translator, + CSP: CSP, } } @@ -75,14 +68,10 @@ func (ic *caIdemixCredential) Load() error { if len(pubKeyBytes) == 0 { return errors.New("Issuer public key file is empty") } - pubKey := &idemix.IssuerPublicKey{} - err = proto.Unmarshal(pubKeyBytes, pubKey) - if err != nil { - return errors.Wrapf(err, "Failed to unmarshal Issuer public key bytes") - } - err = pubKey.Check(ic.curve, ic.t) + + ic.issuerKey, err = ic.CSP.KeyImport(pubKeyBytes, &bccsp.IdemixIssuerPublicKeyImportOpts{Temporary: true, AttributeNames: GetAttributeNames()}) if err != nil { - return errors.Wrapf(err, "Issuer public key check failed") + return errors.Wrapf(err, "Failed to import Issuer key") } privKey, err := os.ReadFile(ic.secretKeyFile) if err != nil { @@ -91,9 +80,10 @@ func (ic *caIdemixCredential) Load() error { if len(privKey) == 0 { return errors.New("Issuer secret key file is empty") } - ic.issuerKey = &idemix.IssuerKey{ - Ipk: pubKey, - Isk: privKey, + + ic.issuerKey, err = ic.CSP.KeyImport(privKey, &bccsp.IdemixIssuerKeyImportOpts{Temporary: true, AttributeNames: GetAttributeNames()}) + if err != nil { + return errors.Wrapf(err, "Failed to import Issuer key") } // TODO: check if issuer key is valid by checking public and secret key pair return nil @@ -102,23 +92,33 @@ func (ic *caIdemixCredential) Load() error { // Store stores the CA's Idemix public and private key to the location // specified by pubKeyFile and secretKeyFile attributes, respectively func (ic *caIdemixCredential) Store() error { - ik, err := ic.GetIssuerKey() + isk, err := ic.GetIssuerKey() if err != nil { return err } - ipkBytes, err := proto.Marshal(ik.Ipk) + ipk, err := isk.PublicKey() + if err != nil { + return errors.Wrapf(err, "Failed to obtain public key") + } + + iskbytes, err := isk.Bytes() + if err != nil { + return errors.New("Failed to convert Issuer private key to bytes") + } + + ipkbytes, err := ipk.Bytes() if err != nil { - return errors.New("Failed to marshal Issuer public key") + return errors.New("Failed to convert Issuer public key to bytes") } - err = util.WriteFile(ic.pubKeyFile, ipkBytes, 0o644) + err = util.WriteFile(ic.pubKeyFile, ipkbytes, 0o644) if err != nil { log.Errorf("Failed to store Issuer public key: %s", err.Error()) return errors.New("Failed to store Issuer public key") } - err = util.WriteFile(ic.secretKeyFile, ik.Isk, 0o644) + err = util.WriteFile(ic.secretKeyFile, iskbytes, 0o644) if err != nil { log.Errorf("Failed to store Issuer secret key: %s", err.Error()) return errors.New("Failed to store Issuer secret key") @@ -131,7 +131,7 @@ func (ic *caIdemixCredential) Store() error { // GetIssuerKey returns idemix.IssuerKey object that is associated with // this CAIdemixCredential -func (ic *caIdemixCredential) GetIssuerKey() (*idemix.IssuerKey, error) { +func (ic *caIdemixCredential) GetIssuerKey() (bccsp.Key, error) { if ic.issuerKey == nil { return nil, errors.New("Issuer credential is not set") } @@ -139,12 +139,12 @@ func (ic *caIdemixCredential) GetIssuerKey() (*idemix.IssuerKey, error) { } // SetIssuerKey sets idemix.IssuerKey object -func (ic *caIdemixCredential) SetIssuerKey(key *idemix.IssuerKey) { +func (ic *caIdemixCredential) SetIssuerKey(key bccsp.Key) { ic.issuerKey = key } // NewIssuerKey creates new Issuer key -func (ic *caIdemixCredential) NewIssuerKey() (*idemix.IssuerKey, error) { +func (ic *caIdemixCredential) NewIssuerKey() (bccsp.Key, error) { // Currently, Idemix library supports these four attributes. The supported attribute names // must also be known when creating issuer key. In the future, Idemix library will support // arbitary attribute names, so removing the need to hardcode attribute names in the issuer @@ -153,7 +153,7 @@ func (ic *caIdemixCredential) NewIssuerKey() (*idemix.IssuerKey, error) { // Role - if the user is admin or member // EnrollmentID - enrollment ID of the user // RevocationHandle - revocation handle of a credential - ik, err := ic.idemixLib.NewIssuerKey(GetAttributeNames()) + ik, err := ic.CSP.KeyGen(&bccsp.IdemixIssuerKeyGenOpts{Temporary: true, AttributeNames: GetAttributeNames()}) if err != nil { return nil, err } diff --git a/lib/server/idemix/issuercredential_test.go b/lib/server/idemix/issuercredential_test.go index 8e326b4fd..7e9f3c2b0 100644 --- a/lib/server/idemix/issuercredential_test.go +++ b/lib/server/idemix/issuercredential_test.go @@ -7,15 +7,12 @@ SPDX-License-Identifier: Apache-2.0 package idemix_test import ( - "fmt" "os" "path" "path/filepath" "testing" - idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" - proto "github.com/golang/protobuf/proto" - cidemix "github.com/hyperledger/fabric-ca/lib/common/idemix" + "github.com/IBM/idemix/bccsp/types" . "github.com/hyperledger/fabric-ca/lib/server/idemix" "github.com/hyperledger/fabric-ca/lib/server/idemix/mocks" "github.com/pkg/errors" @@ -23,15 +20,7 @@ import ( ) func TestLoadEmptyIdemixPublicKey(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testLoadEmptyIdemixPublicKey(t, curve) - }) - } -} - -func testLoadEmptyIdemixPublicKey(t *testing.T, curveID cidemix.CurveID) { - _, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + _, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) @@ -41,8 +30,7 @@ func testLoadEmptyIdemixPublicKey(t *testing.T, curveID cidemix.CurveID) { t.Fatalf("Failed to create temp file: %s", err.Error()) } defer os.RemoveAll(testdir) - idemixLib := new(mocks.Lib) - ic := NewIssuerCredential(pubkeyfile.Name(), testSecretKeyFile, idemixLib, curveID) + ic := NewIssuerCredential(pubkeyfile.Name(), testSecretKeyFile, getCSP(t)) err = ic.Load() assert.Error(t, err, "Should have failed to load non existing issuer public key") if err != nil { @@ -51,14 +39,6 @@ func testLoadEmptyIdemixPublicKey(t *testing.T, curveID cidemix.CurveID) { } func TestLoadFakeIdemixPublicKey(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testLoadFakeIdemixPublicKey(t, curve) - }) - } -} - -func testLoadFakeIdemixPublicKey(t *testing.T, curveID cidemix.CurveID) { testdir := t.TempDir() pubkeyfile, err := os.CreateTemp(testdir, "IdemixPublicKey") if err != nil { @@ -73,25 +53,16 @@ func testLoadFakeIdemixPublicKey(t *testing.T, curveID cidemix.CurveID) { if err != nil { t.Fatalf("Failed to write to the file %s", pubkeyfile.Name()) } - idemixLib := new(mocks.Lib) - ik := NewIssuerCredential(pubkeyfile.Name(), privkeyfile.Name(), idemixLib, curveID) + ik := NewIssuerCredential(pubkeyfile.Name(), privkeyfile.Name(), getCSP(t)) err = ik.Load() assert.Error(t, err, "Should have failed to load non existing issuer public key") if err != nil { - assert.Contains(t, err.Error(), "Failed to unmarshal Issuer public key bytes") + assert.Contains(t, err.Error(), "Failed to import Issuer key") } } func TestLoadEmptyIdemixSecretKey(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testLoadEmptyIdemixSecretKey(t, curve) - }) - } -} - -func testLoadEmptyIdemixSecretKey(t *testing.T, curveID cidemix.CurveID) { - testPublicKeyFile, _, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, _, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) @@ -101,8 +72,7 @@ func testLoadEmptyIdemixSecretKey(t *testing.T, curveID cidemix.CurveID) { t.Fatalf("Failed to create temp file: %s", err.Error()) } defer os.RemoveAll(testdir) - idemixLib := new(mocks.Lib) - ik := NewIssuerCredential(testPublicKeyFile, privkeyfile.Name(), idemixLib, curveID) + ik := NewIssuerCredential(testPublicKeyFile, privkeyfile.Name(), getCSP(t)) err = ik.Load() assert.Error(t, err, "Should have failed to load non existing issuer secret key") if err != nil { @@ -111,21 +81,12 @@ func testLoadEmptyIdemixSecretKey(t *testing.T, curveID cidemix.CurveID) { } func TestLoadNonExistentIdemixSecretKey(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testLoadNonExistentIdemixSecretKey(t, curve) - }) - } -} - -func testLoadNonExistentIdemixSecretKey(t *testing.T, curveID cidemix.CurveID) { - testPublicKeyFile, _, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, _, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) testdir := t.TempDir() - idemixLib := new(mocks.Lib) - ik := NewIssuerCredential(testPublicKeyFile, filepath.Join(testdir, "IdemixSecretKey"), idemixLib, curveID) + ik := NewIssuerCredential(testPublicKeyFile, filepath.Join(testdir, "IdemixSecretKey"), getCSP(t)) err = ik.Load() assert.Error(t, err, "Should have failed to load non existing issuer secret key") if err != nil { @@ -134,20 +95,11 @@ func testLoadNonExistentIdemixSecretKey(t *testing.T, curveID cidemix.CurveID) { } func TestLoad(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testLoad(t, curve) - }) - } -} - -func testLoad(t *testing.T, curveID cidemix.CurveID) { - testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) - idemixLib := new(mocks.Lib) - ik := NewIssuerCredential(testPublicKeyFile, testSecretKeyFile, idemixLib, curveID) + ik := NewIssuerCredential(testPublicKeyFile, testSecretKeyFile, getCSP(t)) err = ik.Load() assert.NoError(t, err, "Failed to load Idemix issuer credential") @@ -156,20 +108,11 @@ func testLoad(t *testing.T, curveID cidemix.CurveID) { } func TestStoreNilIssuerKey(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testStoreNilIssuerKey(t, curve) - }) - } -} - -func testStoreNilIssuerKey(t *testing.T, curveID cidemix.CurveID) { - testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) - idemixLib := new(mocks.Lib) - ik := NewIssuerCredential(testPublicKeyFile, testSecretKeyFile, idemixLib, curveID) + ik := NewIssuerCredential(testPublicKeyFile, testSecretKeyFile, getCSP(t)) err = ik.Store() assert.Error(t, err, "Should fail if store is called without setting the issuer key or loading the issuer key from disk") if err != nil { @@ -178,38 +121,26 @@ func testStoreNilIssuerKey(t *testing.T, curveID cidemix.CurveID) { } func TestStoreNilIdemixPublicKey(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testStoreNilIdemixPublicKey(t, curve) - }) - } -} - -func testStoreNilIdemixPublicKey(t *testing.T, curveID cidemix.CurveID) { - testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) - idemixLib := new(mocks.Lib) - ik := NewIssuerCredential(testPublicKeyFile, testSecretKeyFile, idemixLib, curveID) - ik.SetIssuerKey(&idemix.IssuerKey{}) + ik := NewIssuerCredential(testPublicKeyFile, testSecretKeyFile, getCSP(t)) + mockSKey := new(mocks.BccspKey) + mockPKey := new(mocks.BccspKey) + mockPKey.On("Bytes").Return(nil, errors.New("bad bad")) + mockSKey.On("Bytes").Return(nil, errors.New("bad bad")) + mockSKey.On("PublicKey").Return(mockPKey, nil) + ik.SetIssuerKey(mockSKey) err = ik.Store() assert.Error(t, err, "Should fail if store is called with empty issuer public key byte array") if err != nil { - assert.Equal(t, err.Error(), "Failed to marshal Issuer public key") + assert.Equal(t, err.Error(), "Failed to convert Issuer private key to bytes") } } func TestStoreReadonlyPublicKeyFilePath(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testStoreReadonlyPublicKeyFilePath(t, curve) - }) - } -} - -func testStoreReadonlyPublicKeyFilePath(t *testing.T, curveID cidemix.CurveID) { - testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) @@ -223,18 +154,15 @@ func testStoreReadonlyPublicKeyFilePath(t *testing.T, curveID cidemix.CurveID) { t.Fatalf("Failed to read idemix public key file %s", validPubKeyFile) } - pubKey := &idemix.IssuerPublicKey{} - err = proto.Unmarshal(pubKeyBytes, pubKey) - if err != nil { - t.Fatalf("Failed to unmarshal idemix public key bytes from %s", validPubKeyFile) - } - idemixLib := new(mocks.Lib) + ipk, err := getCSP(t).KeyImport(pubKeyBytes, &types.IdemixIssuerPublicKeyImportOpts{Temporary: true}) + assert.NoError(t, err) + err = os.MkdirAll(path.Dir(pubkeyfile), 4444) if err != nil { t.Fatalf("Failed to create read only directory: %s", err.Error()) } - ik := NewIssuerCredential(pubkeyfile, testSecretKeyFile, idemixLib, curveID) - ik.SetIssuerKey(&idemix.IssuerKey{Ipk: pubKey}) + ik := NewIssuerCredential(pubkeyfile, testSecretKeyFile, getCSP(t)) + ik.SetIssuerKey(ipk) err = ik.Store() assert.Error(t, err, "Should fail if issuer public key is being stored to readonly directory") if err != nil { @@ -243,15 +171,7 @@ func testStoreReadonlyPublicKeyFilePath(t *testing.T, curveID cidemix.CurveID) { } func TestStoreReadonlySecretKeyFilePath(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testStoreReadonlySecretKeyFilePath(t, curve) - }) - } -} - -func testStoreReadonlySecretKeyFilePath(t *testing.T, curveID cidemix.CurveID) { - testPublicKeyFile, _, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, _, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) @@ -265,18 +185,15 @@ func testStoreReadonlySecretKeyFilePath(t *testing.T, curveID cidemix.CurveID) { t.Fatalf("Failed to read idemix public key file %s", testPublicKeyFile) } - pubKey := &idemix.IssuerPublicKey{} - err = proto.Unmarshal(pubKeyBytes, pubKey) - if err != nil { - t.Fatalf("Failed to unmarshal idemix public key bytes from %s", testPublicKeyFile) - } - idemixLib := new(mocks.Lib) + ipk, err := getCSP(t).KeyImport(pubKeyBytes, &types.IdemixIssuerPublicKeyImportOpts{Temporary: true}) + assert.NoError(t, err) + err = os.MkdirAll(path.Dir(privkeyfile), 4444) if err != nil { t.Fatalf("Failed to create read only directory: %s", err.Error()) } - ik := NewIssuerCredential(testPublicKeyFile, privkeyfile, idemixLib, curveID) - ik.SetIssuerKey(&idemix.IssuerKey{Ipk: pubKey}) + ik := NewIssuerCredential(testPublicKeyFile, privkeyfile, getCSP(t)) + ik.SetIssuerKey(ipk) err = ik.Store() assert.Error(t, err, "Should fail if issuer secret key is being stored to read-only directory") if err != nil { @@ -285,20 +202,11 @@ func testStoreReadonlySecretKeyFilePath(t *testing.T, curveID cidemix.CurveID) { } func TestGetIssuerKey(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testGetIssuerKey(t, curve) - }) - } -} - -func testGetIssuerKey(t *testing.T, curveID cidemix.CurveID) { - testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) - idemixLib := new(mocks.Lib) - ik := NewIssuerCredential(testPublicKeyFile, testSecretKeyFile, idemixLib, curveID) + ik := NewIssuerCredential(testPublicKeyFile, testSecretKeyFile, getCSP(t)) _, err = ik.GetIssuerKey() assert.Error(t, err, "GetIssuerKey should return an error if it is called without setting the issuer key or loading the issuer key from disk") if err != nil { @@ -313,57 +221,24 @@ func testGetIssuerKey(t *testing.T, curveID cidemix.CurveID) { } func TestNewIssuerKeyError(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testNewIssuerKeyError(t, curve) - }) - } -} - -func testNewIssuerKeyError(t *testing.T, curveID cidemix.CurveID) { - testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) - idemixLib := new(mocks.Lib) - rnd, err := cidemix.CurveByID(curveID).Rand() - if err != nil { - t.Fatalf("Failed to generate a random number: %s", err.Error()) - } - idemixLib.On("GetRand").Return(rnd, nil) - idemixLib.On("NewIssuerKey", GetAttributeNames()).Return(nil, errors.New("Failed to create new issuer key")) - ic := NewIssuerCredential(testPublicKeyFile, testSecretKeyFile, idemixLib, curveID) + mockCsp := new(mocks.BccspBCCSP) + mockCsp.On("KeyGen", &types.IdemixIssuerKeyGenOpts{Temporary: true, AttributeNames: []string{"OU", "Role", "EnrollmentID", "RevocationHandle"}}).Return(nil, errors.New("ajajaja")) + + ic := NewIssuerCredential(testPublicKeyFile, testSecretKeyFile, mockCsp) _, err = ic.NewIssuerKey() assert.Error(t, err) } func TestNewIssuerKey(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testNewIssuerKey(t, curve) - }) - } -} - -func testNewIssuerKey(t *testing.T, curveID cidemix.CurveID) { - testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t, curveID) + testPublicKeyFile, testSecretKeyFile, tmpDir, err := GeneratePublicPrivateKeyPair(t) assert.NoError(t, err) defer os.RemoveAll(tmpDir) - idemixLib := new(mocks.Lib) - idemix := NewLib(curveID) - rnd, err := cidemix.CurveByID(curveID).Rand() - if err != nil { - t.Fatalf("Failed to generate a random number: %s", err.Error()) - } - attrNames := GetAttributeNames() - ik, err := idemix.NewIssuerKey(attrNames) - if err != nil { - t.Fatalf("Failed to create new issuer key: %s", err.Error()) - } - idemixLib.On("GetRand").Return(rnd, nil) - idemixLib.On("NewIssuerKey", attrNames).Return(ik, nil) - ic := NewIssuerCredential(testPublicKeyFile, testSecretKeyFile, idemixLib, curveID) + ic := NewIssuerCredential(testPublicKeyFile, testSecretKeyFile, getCSP(t)) _, err = ic.NewIssuerKey() assert.NoError(t, err) } diff --git a/lib/server/idemix/mocks/Clock.go b/lib/server/idemix/mocks/Clock.go deleted file mode 100644 index 3b077bb8c..000000000 --- a/lib/server/idemix/mocks/Clock.go +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright IBM Corp. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ -// Code generated by mockery v1.0.0 - -package mocks - -import mock "github.com/stretchr/testify/mock" -import time "time" - -// Clock is an autogenerated mock type for the Clock type -type Clock struct { - mock.Mock -} - -// Now provides a mock function with given fields: -func (_m *Clock) Now() time.Time { - ret := _m.Called() - - var r0 time.Time - if rf, ok := ret.Get(0).(func() time.Time); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(time.Time) - } - - return r0 -} diff --git a/lib/server/idemix/mocks/IssuerCredential.go b/lib/server/idemix/mocks/IssuerCredential.go deleted file mode 100644 index 41282cfe8..000000000 --- a/lib/server/idemix/mocks/IssuerCredential.go +++ /dev/null @@ -1,97 +0,0 @@ -/* -Copyright IBM Corp. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ -// Code generated by mockery v1.0.0 - -package mocks - -import ( - idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" - mock "github.com/stretchr/testify/mock" -) - -// IssuerCredential is an autogenerated mock type for the IssuerCredential type -type IssuerCredential struct { - mock.Mock -} - -// GetIssuerKey provides a mock function with given fields: -func (_m *IssuerCredential) GetIssuerKey() (*idemix.IssuerKey, error) { - ret := _m.Called() - - var r0 *idemix.IssuerKey - if rf, ok := ret.Get(0).(func() *idemix.IssuerKey); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*idemix.IssuerKey) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Load provides a mock function with given fields: -func (_m *IssuerCredential) Load() error { - ret := _m.Called() - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// NewIssuerKey provides a mock function with given fields: -func (_m *IssuerCredential) NewIssuerKey() (*idemix.IssuerKey, error) { - ret := _m.Called() - - var r0 *idemix.IssuerKey - if rf, ok := ret.Get(0).(func() *idemix.IssuerKey); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*idemix.IssuerKey) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// SetIssuerKey provides a mock function with given fields: key -func (_m *IssuerCredential) SetIssuerKey(key *idemix.IssuerKey) { - _m.Called(key) -} - -// Store provides a mock function with given fields: -func (_m *IssuerCredential) Store() error { - ret := _m.Called() - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/lib/server/idemix/mocks/Lib.go b/lib/server/idemix/mocks/Lib.go deleted file mode 100644 index f4c6fb531..000000000 --- a/lib/server/idemix/mocks/Lib.go +++ /dev/null @@ -1,132 +0,0 @@ -// Code generated by mockery v2.7.4. DO NOT EDIT. - -package mocks - -import ( - ecdsa "crypto/ecdsa" - - idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" - math "github.com/IBM/mathlib" - - mock "github.com/stretchr/testify/mock" -) - -// Lib is an autogenerated mock type for the Lib type -type Lib struct { - mock.Mock -} - -// CreateCRI provides a mock function with given fields: key, unrevokedHandles, epoch, alg -func (_m *Lib) CreateCRI(key *ecdsa.PrivateKey, unrevokedHandles []*math.Zr, epoch int, alg idemix.RevocationAlgorithm) (*idemix.CredentialRevocationInformation, error) { - ret := _m.Called(key, unrevokedHandles, epoch, alg) - - var r0 *idemix.CredentialRevocationInformation - if rf, ok := ret.Get(0).(func(*ecdsa.PrivateKey, []*math.Zr, int, idemix.RevocationAlgorithm) *idemix.CredentialRevocationInformation); ok { - r0 = rf(key, unrevokedHandles, epoch, alg) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*idemix.CredentialRevocationInformation) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(*ecdsa.PrivateKey, []*math.Zr, int, idemix.RevocationAlgorithm) error); ok { - r1 = rf(key, unrevokedHandles, epoch, alg) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GenerateLongTermRevocationKey provides a mock function with given fields: -func (_m *Lib) GenerateLongTermRevocationKey() (*ecdsa.PrivateKey, error) { - ret := _m.Called() - - var r0 *ecdsa.PrivateKey - if rf, ok := ret.Get(0).(func() *ecdsa.PrivateKey); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*ecdsa.PrivateKey) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewCredential provides a mock function with given fields: key, m, attrs -func (_m *Lib) NewCredential(key *idemix.IssuerKey, m *idemix.CredRequest, attrs []*math.Zr) (*idemix.Credential, error) { - ret := _m.Called(key, m, attrs) - - var r0 *idemix.Credential - if rf, ok := ret.Get(0).(func(*idemix.IssuerKey, *idemix.CredRequest, []*math.Zr) *idemix.Credential); ok { - r0 = rf(key, m, attrs) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*idemix.Credential) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(*idemix.IssuerKey, *idemix.CredRequest, []*math.Zr) error); ok { - r1 = rf(key, m, attrs) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewIssuerKey provides a mock function with given fields: AttributeNames -func (_m *Lib) NewIssuerKey(AttributeNames []string) (*idemix.IssuerKey, error) { - ret := _m.Called(AttributeNames) - - var r0 *idemix.IssuerKey - if rf, ok := ret.Get(0).(func([]string) *idemix.IssuerKey); ok { - r0 = rf(AttributeNames) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*idemix.IssuerKey) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func([]string) error); ok { - r1 = rf(AttributeNames) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RandModOrder provides a mock function with given fields: -func (_m *Lib) RandModOrder() (*math.Zr, error) { - ret := _m.Called() - - var r0 *math.Zr - if rf, ok := ret.Get(0).(func() *math.Zr); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*math.Zr) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/lib/server/idemix/mocks/Result.go b/lib/server/idemix/mocks/Result.go deleted file mode 100644 index 52baea862..000000000 --- a/lib/server/idemix/mocks/Result.go +++ /dev/null @@ -1,57 +0,0 @@ -/* -Copyright IBM Corp. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ -// Code generated by mockery v1.0.0 - -package mocks - -import mock "github.com/stretchr/testify/mock" - -// Result is an autogenerated mock type for the Result type -type Result struct { - mock.Mock -} - -// LastInsertId provides a mock function with given fields: -func (_m *Result) LastInsertId() (int64, error) { - ret := _m.Called() - - var r0 int64 - if rf, ok := ret.Get(0).(func() int64); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int64) - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// RowsAffected provides a mock function with given fields: -func (_m *Result) RowsAffected() (int64, error) { - ret := _m.Called() - - var r0 int64 - if rf, ok := ret.Get(0).(func() int64); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int64) - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/lib/server/idemix/mocks/RevocationAuthority.go b/lib/server/idemix/mocks/RevocationAuthority.go deleted file mode 100644 index f451da20e..000000000 --- a/lib/server/idemix/mocks/RevocationAuthority.go +++ /dev/null @@ -1,100 +0,0 @@ -// Code generated by mockery v2.7.4. DO NOT EDIT. - -package mocks - -import ( - ecdsa "crypto/ecdsa" - - idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" - math "github.com/IBM/mathlib" - - mock "github.com/stretchr/testify/mock" -) - -// RevocationAuthority is an autogenerated mock type for the RevocationAuthority type -type RevocationAuthority struct { - mock.Mock -} - -// CreateCRI provides a mock function with given fields: -func (_m *RevocationAuthority) CreateCRI() (*idemix.CredentialRevocationInformation, error) { - ret := _m.Called() - - var r0 *idemix.CredentialRevocationInformation - if rf, ok := ret.Get(0).(func() *idemix.CredentialRevocationInformation); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*idemix.CredentialRevocationInformation) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Epoch provides a mock function with given fields: -func (_m *RevocationAuthority) Epoch() (int, error) { - ret := _m.Called() - - var r0 int - if rf, ok := ret.Get(0).(func() int); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(int) - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// GetNewRevocationHandle provides a mock function with given fields: -func (_m *RevocationAuthority) GetNewRevocationHandle() (*math.Zr, error) { - ret := _m.Called() - - var r0 *math.Zr - if rf, ok := ret.Get(0).(func() *math.Zr); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*math.Zr) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// PublicKey provides a mock function with given fields: -func (_m *RevocationAuthority) PublicKey() *ecdsa.PublicKey { - ret := _m.Called() - - var r0 *ecdsa.PublicKey - if rf, ok := ret.Get(0).(func() *ecdsa.PublicKey); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*ecdsa.PublicKey) - } - } - - return r0 -} diff --git a/lib/server/idemix/mocks/bccsp_bccsp.go b/lib/server/idemix/mocks/bccsp_bccsp.go new file mode 100644 index 000000000..73a36c9f2 --- /dev/null +++ b/lib/server/idemix/mocks/bccsp_bccsp.go @@ -0,0 +1,288 @@ +// Code generated by mockery v2.33.0. DO NOT EDIT. + +package mocks + +import ( + hash "hash" + + mock "github.com/stretchr/testify/mock" + + types "github.com/IBM/idemix/bccsp/types" +) + +// BccspBCCSP is an autogenerated mock type for the BccspBCCSP type +type BccspBCCSP struct { + mock.Mock +} + +// Decrypt provides a mock function with given fields: k, ciphertext, opts +func (_m *BccspBCCSP) Decrypt(k types.Key, ciphertext []byte, opts types.DecrypterOpts) ([]byte, error) { + ret := _m.Called(k, ciphertext, opts) + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(types.Key, []byte, types.DecrypterOpts) ([]byte, error)); ok { + return rf(k, ciphertext, opts) + } + if rf, ok := ret.Get(0).(func(types.Key, []byte, types.DecrypterOpts) []byte); ok { + r0 = rf(k, ciphertext, opts) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(types.Key, []byte, types.DecrypterOpts) error); ok { + r1 = rf(k, ciphertext, opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Encrypt provides a mock function with given fields: k, plaintext, opts +func (_m *BccspBCCSP) Encrypt(k types.Key, plaintext []byte, opts types.EncrypterOpts) ([]byte, error) { + ret := _m.Called(k, plaintext, opts) + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(types.Key, []byte, types.EncrypterOpts) ([]byte, error)); ok { + return rf(k, plaintext, opts) + } + if rf, ok := ret.Get(0).(func(types.Key, []byte, types.EncrypterOpts) []byte); ok { + r0 = rf(k, plaintext, opts) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(types.Key, []byte, types.EncrypterOpts) error); ok { + r1 = rf(k, plaintext, opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetHash provides a mock function with given fields: opts +func (_m *BccspBCCSP) GetHash(opts types.HashOpts) (hash.Hash, error) { + ret := _m.Called(opts) + + var r0 hash.Hash + var r1 error + if rf, ok := ret.Get(0).(func(types.HashOpts) (hash.Hash, error)); ok { + return rf(opts) + } + if rf, ok := ret.Get(0).(func(types.HashOpts) hash.Hash); ok { + r0 = rf(opts) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(hash.Hash) + } + } + + if rf, ok := ret.Get(1).(func(types.HashOpts) error); ok { + r1 = rf(opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetKey provides a mock function with given fields: ski +func (_m *BccspBCCSP) GetKey(ski []byte) (types.Key, error) { + ret := _m.Called(ski) + + var r0 types.Key + var r1 error + if rf, ok := ret.Get(0).(func([]byte) (types.Key, error)); ok { + return rf(ski) + } + if rf, ok := ret.Get(0).(func([]byte) types.Key); ok { + r0 = rf(ski) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(types.Key) + } + } + + if rf, ok := ret.Get(1).(func([]byte) error); ok { + r1 = rf(ski) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Hash provides a mock function with given fields: msg, opts +func (_m *BccspBCCSP) Hash(msg []byte, opts types.HashOpts) ([]byte, error) { + ret := _m.Called(msg, opts) + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func([]byte, types.HashOpts) ([]byte, error)); ok { + return rf(msg, opts) + } + if rf, ok := ret.Get(0).(func([]byte, types.HashOpts) []byte); ok { + r0 = rf(msg, opts) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func([]byte, types.HashOpts) error); ok { + r1 = rf(msg, opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// KeyDeriv provides a mock function with given fields: k, opts +func (_m *BccspBCCSP) KeyDeriv(k types.Key, opts types.KeyDerivOpts) (types.Key, error) { + ret := _m.Called(k, opts) + + var r0 types.Key + var r1 error + if rf, ok := ret.Get(0).(func(types.Key, types.KeyDerivOpts) (types.Key, error)); ok { + return rf(k, opts) + } + if rf, ok := ret.Get(0).(func(types.Key, types.KeyDerivOpts) types.Key); ok { + r0 = rf(k, opts) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(types.Key) + } + } + + if rf, ok := ret.Get(1).(func(types.Key, types.KeyDerivOpts) error); ok { + r1 = rf(k, opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// KeyGen provides a mock function with given fields: opts +func (_m *BccspBCCSP) KeyGen(opts types.KeyGenOpts) (types.Key, error) { + ret := _m.Called(opts) + + var r0 types.Key + var r1 error + if rf, ok := ret.Get(0).(func(types.KeyGenOpts) (types.Key, error)); ok { + return rf(opts) + } + if rf, ok := ret.Get(0).(func(types.KeyGenOpts) types.Key); ok { + r0 = rf(opts) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(types.Key) + } + } + + if rf, ok := ret.Get(1).(func(types.KeyGenOpts) error); ok { + r1 = rf(opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// KeyImport provides a mock function with given fields: raw, opts +func (_m *BccspBCCSP) KeyImport(raw interface{}, opts types.KeyImportOpts) (types.Key, error) { + ret := _m.Called(raw, opts) + + var r0 types.Key + var r1 error + if rf, ok := ret.Get(0).(func(interface{}, types.KeyImportOpts) (types.Key, error)); ok { + return rf(raw, opts) + } + if rf, ok := ret.Get(0).(func(interface{}, types.KeyImportOpts) types.Key); ok { + r0 = rf(raw, opts) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(types.Key) + } + } + + if rf, ok := ret.Get(1).(func(interface{}, types.KeyImportOpts) error); ok { + r1 = rf(raw, opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Sign provides a mock function with given fields: k, digest, opts +func (_m *BccspBCCSP) Sign(k types.Key, digest []byte, opts types.SignerOpts) ([]byte, error) { + ret := _m.Called(k, digest, opts) + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func(types.Key, []byte, types.SignerOpts) ([]byte, error)); ok { + return rf(k, digest, opts) + } + if rf, ok := ret.Get(0).(func(types.Key, []byte, types.SignerOpts) []byte); ok { + r0 = rf(k, digest, opts) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func(types.Key, []byte, types.SignerOpts) error); ok { + r1 = rf(k, digest, opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Verify provides a mock function with given fields: k, signature, digest, opts +func (_m *BccspBCCSP) Verify(k types.Key, signature []byte, digest []byte, opts types.SignerOpts) (bool, error) { + ret := _m.Called(k, signature, digest, opts) + + var r0 bool + var r1 error + if rf, ok := ret.Get(0).(func(types.Key, []byte, []byte, types.SignerOpts) (bool, error)); ok { + return rf(k, signature, digest, opts) + } + if rf, ok := ret.Get(0).(func(types.Key, []byte, []byte, types.SignerOpts) bool); ok { + r0 = rf(k, signature, digest, opts) + } else { + r0 = ret.Get(0).(bool) + } + + if rf, ok := ret.Get(1).(func(types.Key, []byte, []byte, types.SignerOpts) error); ok { + r1 = rf(k, signature, digest, opts) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewBccspBCCSP creates a new instance of BccspBCCSP. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewBccspBCCSP(t interface { + mock.TestingT + Cleanup(func()) +}) *BccspBCCSP { + mock := &BccspBCCSP{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/lib/server/idemix/mocks/bccsp_key.go b/lib/server/idemix/mocks/bccsp_key.go new file mode 100644 index 000000000..e1ad66d27 --- /dev/null +++ b/lib/server/idemix/mocks/bccsp_key.go @@ -0,0 +1,123 @@ +// Code generated by mockery v2.33.0. DO NOT EDIT. + +package mocks + +import ( + types "github.com/IBM/idemix/bccsp/types" + mock "github.com/stretchr/testify/mock" +) + +// BccspKey is an autogenerated mock type for the BccspKey type +type BccspKey struct { + mock.Mock +} + +// Bytes provides a mock function with given fields: +func (_m *BccspKey) Bytes() ([]byte, error) { + ret := _m.Called() + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []byte); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Private provides a mock function with given fields: +func (_m *BccspKey) Private() bool { + ret := _m.Called() + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// PublicKey provides a mock function with given fields: +func (_m *BccspKey) PublicKey() (types.Key, error) { + ret := _m.Called() + + var r0 types.Key + var r1 error + if rf, ok := ret.Get(0).(func() (types.Key, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() types.Key); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(types.Key) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SKI provides a mock function with given fields: +func (_m *BccspKey) SKI() []byte { + ret := _m.Called() + + var r0 []byte + if rf, ok := ret.Get(0).(func() []byte); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + return r0 +} + +// Symmetric provides a mock function with given fields: +func (_m *BccspKey) Symmetric() bool { + ret := _m.Called() + + var r0 bool + if rf, ok := ret.Get(0).(func() bool); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// NewBccspKey creates a new instance of BccspKey. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewBccspKey(t interface { + mock.TestingT + Cleanup(func()) +}) *BccspKey { + mock := &BccspKey{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/lib/server/idemix/mocks/clock.go b/lib/server/idemix/mocks/clock.go new file mode 100644 index 000000000..240ca6062 --- /dev/null +++ b/lib/server/idemix/mocks/clock.go @@ -0,0 +1,42 @@ +// Code generated by mockery v2.33.0. DO NOT EDIT. + +package mocks + +import ( + time "time" + + mock "github.com/stretchr/testify/mock" +) + +// Clock is an autogenerated mock type for the Clock type +type Clock struct { + mock.Mock +} + +// Now provides a mock function with given fields: +func (_m *Clock) Now() time.Time { + ret := _m.Called() + + var r0 time.Time + if rf, ok := ret.Get(0).(func() time.Time); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(time.Time) + } + + return r0 +} + +// NewClock creates a new instance of Clock. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewClock(t interface { + mock.TestingT + Cleanup(func()) +}) *Clock { + mock := &Clock{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/lib/server/idemix/mocks/cred_db_accessor.go b/lib/server/idemix/mocks/cred_db_accessor.go index 1e6906e88..fae3bd427 100644 --- a/lib/server/idemix/mocks/cred_db_accessor.go +++ b/lib/server/idemix/mocks/cred_db_accessor.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.7.4. DO NOT EDIT. +// Code generated by mockery v2.33.0. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ func (_m *CredDBAccessor) GetCredential(revocationHandle string) (*idemix.CredRe ret := _m.Called(revocationHandle) var r0 *idemix.CredRecord + var r1 error + if rf, ok := ret.Get(0).(func(string) (*idemix.CredRecord, error)); ok { + return rf(revocationHandle) + } if rf, ok := ret.Get(0).(func(string) *idemix.CredRecord); ok { r0 = rf(revocationHandle) } else { @@ -27,7 +31,6 @@ func (_m *CredDBAccessor) GetCredential(revocationHandle string) (*idemix.CredRe } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(revocationHandle) } else { @@ -42,6 +45,10 @@ func (_m *CredDBAccessor) GetCredentialsByID(id string) ([]idemix.CredRecord, er ret := _m.Called(id) var r0 []idemix.CredRecord + var r1 error + if rf, ok := ret.Get(0).(func(string) ([]idemix.CredRecord, error)); ok { + return rf(id) + } if rf, ok := ret.Get(0).(func(string) []idemix.CredRecord); ok { r0 = rf(id) } else { @@ -50,7 +57,6 @@ func (_m *CredDBAccessor) GetCredentialsByID(id string) ([]idemix.CredRecord, er } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(id) } else { @@ -65,6 +71,10 @@ func (_m *CredDBAccessor) GetRevokedCredentials() ([]idemix.CredRecord, error) { ret := _m.Called() var r0 []idemix.CredRecord + var r1 error + if rf, ok := ret.Get(0).(func() ([]idemix.CredRecord, error)); ok { + return rf() + } if rf, ok := ret.Get(0).(func() []idemix.CredRecord); ok { r0 = rf() } else { @@ -73,7 +83,6 @@ func (_m *CredDBAccessor) GetRevokedCredentials() ([]idemix.CredRecord, error) { } } - var r1 error if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { @@ -101,3 +110,17 @@ func (_m *CredDBAccessor) InsertCredential(cr idemix.CredRecord) error { func (_m *CredDBAccessor) SetDB(_a0 db.FabricCADB) { _m.Called(_a0) } + +// NewCredDBAccessor creates a new instance of CredDBAccessor. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewCredDBAccessor(t interface { + mock.TestingT + Cleanup(func()) +}) *CredDBAccessor { + mock := &CredDBAccessor{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/lib/server/idemix/mocks/FabricCATx.go b/lib/server/idemix/mocks/db_fabric_ca_tx.go similarity index 63% rename from lib/server/idemix/mocks/FabricCATx.go rename to lib/server/idemix/mocks/db_fabric_ca_tx.go index 8c4128807..905669d4a 100644 --- a/lib/server/idemix/mocks/FabricCATx.go +++ b/lib/server/idemix/mocks/db_fabric_ca_tx.go @@ -1,17 +1,22 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. +// Code generated by mockery v2.33.0. DO NOT EDIT. + package mocks -import mock "github.com/stretchr/testify/mock" -import sql "database/sql" -import sqlx "github.com/jmoiron/sqlx" +import ( + sql "database/sql" + + mock "github.com/stretchr/testify/mock" -// FabricCATx is an autogenerated mock type for the FabricCATx type -type FabricCATx struct { + sqlx "github.com/jmoiron/sqlx" +) + +// DbFabricCATx is an autogenerated mock type for the DbFabricCATx type +type DbFabricCATx struct { mock.Mock } // Commit provides a mock function with given fields: funcName -func (_m *FabricCATx) Commit(funcName string) error { +func (_m *DbFabricCATx) Commit(funcName string) error { ret := _m.Called(funcName) var r0 error @@ -25,13 +30,17 @@ func (_m *FabricCATx) Commit(funcName string) error { } // Exec provides a mock function with given fields: funcName, query, args -func (_m *FabricCATx) Exec(funcName string, query string, args ...interface{}) (sql.Result, error) { +func (_m *DbFabricCATx) Exec(funcName string, query string, args ...interface{}) (sql.Result, error) { var _ca []interface{} _ca = append(_ca, funcName, query) _ca = append(_ca, args...) ret := _m.Called(_ca...) var r0 sql.Result + var r1 error + if rf, ok := ret.Get(0).(func(string, string, ...interface{}) (sql.Result, error)); ok { + return rf(funcName, query, args...) + } if rf, ok := ret.Get(0).(func(string, string, ...interface{}) sql.Result); ok { r0 = rf(funcName, query, args...) } else { @@ -40,7 +49,6 @@ func (_m *FabricCATx) Exec(funcName string, query string, args ...interface{}) ( } } - var r1 error if rf, ok := ret.Get(1).(func(string, string, ...interface{}) error); ok { r1 = rf(funcName, query, args...) } else { @@ -51,7 +59,7 @@ func (_m *FabricCATx) Exec(funcName string, query string, args ...interface{}) ( } // Get provides a mock function with given fields: funcName, dest, query, args -func (_m *FabricCATx) Get(funcName string, dest interface{}, query string, args ...interface{}) error { +func (_m *DbFabricCATx) Get(funcName string, dest interface{}, query string, args ...interface{}) error { var _ca []interface{} _ca = append(_ca, funcName, dest, query) _ca = append(_ca, args...) @@ -68,13 +76,17 @@ func (_m *FabricCATx) Get(funcName string, dest interface{}, query string, args } // Queryx provides a mock function with given fields: funcName, query, args -func (_m *FabricCATx) Queryx(funcName string, query string, args ...interface{}) (*sqlx.Rows, error) { +func (_m *DbFabricCATx) Queryx(funcName string, query string, args ...interface{}) (*sqlx.Rows, error) { var _ca []interface{} _ca = append(_ca, funcName, query) _ca = append(_ca, args...) ret := _m.Called(_ca...) var r0 *sqlx.Rows + var r1 error + if rf, ok := ret.Get(0).(func(string, string, ...interface{}) (*sqlx.Rows, error)); ok { + return rf(funcName, query, args...) + } if rf, ok := ret.Get(0).(func(string, string, ...interface{}) *sqlx.Rows); ok { r0 = rf(funcName, query, args...) } else { @@ -83,7 +95,6 @@ func (_m *FabricCATx) Queryx(funcName string, query string, args ...interface{}) } } - var r1 error if rf, ok := ret.Get(1).(func(string, string, ...interface{}) error); ok { r1 = rf(funcName, query, args...) } else { @@ -94,7 +105,7 @@ func (_m *FabricCATx) Queryx(funcName string, query string, args ...interface{}) } // Rebind provides a mock function with given fields: query -func (_m *FabricCATx) Rebind(query string) string { +func (_m *DbFabricCATx) Rebind(query string) string { ret := _m.Called(query) var r0 string @@ -108,7 +119,7 @@ func (_m *FabricCATx) Rebind(query string) string { } // Rollback provides a mock function with given fields: funcName -func (_m *FabricCATx) Rollback(funcName string) error { +func (_m *DbFabricCATx) Rollback(funcName string) error { ret := _m.Called(funcName) var r0 error @@ -122,7 +133,7 @@ func (_m *FabricCATx) Rollback(funcName string) error { } // Select provides a mock function with given fields: funcName, dest, query, args -func (_m *FabricCATx) Select(funcName string, dest interface{}, query string, args ...interface{}) error { +func (_m *DbFabricCATx) Select(funcName string, dest interface{}, query string, args ...interface{}) error { var _ca []interface{} _ca = append(_ca, funcName, dest, query) _ca = append(_ca, args...) @@ -137,3 +148,17 @@ func (_m *FabricCATx) Select(funcName string, dest interface{}, query string, ar return r0 } + +// NewDbFabricCATx creates a new instance of DbFabricCATx. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewDbFabricCATx(t interface { + mock.TestingT + Cleanup(func()) +}) *DbFabricCATx { + mock := &DbFabricCATx{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/lib/server/idemix/mocks/fabric_cadb.go b/lib/server/idemix/mocks/db_fabric_cadb.go similarity index 66% rename from lib/server/idemix/mocks/fabric_cadb.go rename to lib/server/idemix/mocks/db_fabric_cadb.go index e42be55b5..3d5d8c554 100644 --- a/lib/server/idemix/mocks/fabric_cadb.go +++ b/lib/server/idemix/mocks/db_fabric_cadb.go @@ -1,20 +1,26 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. +// Code generated by mockery v2.33.0. DO NOT EDIT. package mocks -import context "context" -import db "github.com/hyperledger/fabric-ca/lib/server/db" -import mock "github.com/stretchr/testify/mock" -import sql "database/sql" -import sqlx "github.com/jmoiron/sqlx" +import ( + context "context" -// FabricCADB is an autogenerated mock type for the FabricCADB type -type FabricCADB struct { + db "github.com/hyperledger/fabric-ca/lib/server/db" + + mock "github.com/stretchr/testify/mock" + + sql "database/sql" + + sqlx "github.com/jmoiron/sqlx" +) + +// DbFabricCADB is an autogenerated mock type for the DbFabricCADB type +type DbFabricCADB struct { mock.Mock } // BeginTx provides a mock function with given fields: -func (_m *FabricCADB) BeginTx() db.FabricCATx { +func (_m *DbFabricCADB) BeginTx() db.FabricCATx { ret := _m.Called() var r0 db.FabricCATx @@ -30,7 +36,7 @@ func (_m *FabricCADB) BeginTx() db.FabricCATx { } // Close provides a mock function with given fields: -func (_m *FabricCADB) Close() error { +func (_m *DbFabricCADB) Close() error { ret := _m.Called() var r0 error @@ -44,7 +50,7 @@ func (_m *FabricCADB) Close() error { } // DriverName provides a mock function with given fields: -func (_m *FabricCADB) DriverName() string { +func (_m *DbFabricCADB) DriverName() string { ret := _m.Called() var r0 string @@ -58,13 +64,17 @@ func (_m *FabricCADB) DriverName() string { } // Exec provides a mock function with given fields: funcName, query, args -func (_m *FabricCADB) Exec(funcName string, query string, args ...interface{}) (sql.Result, error) { +func (_m *DbFabricCADB) Exec(funcName string, query string, args ...interface{}) (sql.Result, error) { var _ca []interface{} _ca = append(_ca, funcName, query) _ca = append(_ca, args...) ret := _m.Called(_ca...) var r0 sql.Result + var r1 error + if rf, ok := ret.Get(0).(func(string, string, ...interface{}) (sql.Result, error)); ok { + return rf(funcName, query, args...) + } if rf, ok := ret.Get(0).(func(string, string, ...interface{}) sql.Result); ok { r0 = rf(funcName, query, args...) } else { @@ -73,7 +83,6 @@ func (_m *FabricCADB) Exec(funcName string, query string, args ...interface{}) ( } } - var r1 error if rf, ok := ret.Get(1).(func(string, string, ...interface{}) error); ok { r1 = rf(funcName, query, args...) } else { @@ -84,7 +93,7 @@ func (_m *FabricCADB) Exec(funcName string, query string, args ...interface{}) ( } // Get provides a mock function with given fields: funcName, dest, query, args -func (_m *FabricCADB) Get(funcName string, dest interface{}, query string, args ...interface{}) error { +func (_m *DbFabricCADB) Get(funcName string, dest interface{}, query string, args ...interface{}) error { var _ca []interface{} _ca = append(_ca, funcName, dest, query) _ca = append(_ca, args...) @@ -101,7 +110,7 @@ func (_m *FabricCADB) Get(funcName string, dest interface{}, query string, args } // IsInitialized provides a mock function with given fields: -func (_m *FabricCADB) IsInitialized() bool { +func (_m *DbFabricCADB) IsInitialized() bool { ret := _m.Called() var r0 bool @@ -115,7 +124,7 @@ func (_m *FabricCADB) IsInitialized() bool { } // MustBegin provides a mock function with given fields: -func (_m *FabricCADB) MustBegin() *sqlx.Tx { +func (_m *DbFabricCADB) MustBegin() *sqlx.Tx { ret := _m.Called() var r0 *sqlx.Tx @@ -131,10 +140,14 @@ func (_m *FabricCADB) MustBegin() *sqlx.Tx { } // NamedExec provides a mock function with given fields: funcName, query, arg -func (_m *FabricCADB) NamedExec(funcName string, query string, arg interface{}) (sql.Result, error) { +func (_m *DbFabricCADB) NamedExec(funcName string, query string, arg interface{}) (sql.Result, error) { ret := _m.Called(funcName, query, arg) var r0 sql.Result + var r1 error + if rf, ok := ret.Get(0).(func(string, string, interface{}) (sql.Result, error)); ok { + return rf(funcName, query, arg) + } if rf, ok := ret.Get(0).(func(string, string, interface{}) sql.Result); ok { r0 = rf(funcName, query, arg) } else { @@ -143,7 +156,6 @@ func (_m *FabricCADB) NamedExec(funcName string, query string, arg interface{}) } } - var r1 error if rf, ok := ret.Get(1).(func(string, string, interface{}) error); ok { r1 = rf(funcName, query, arg) } else { @@ -154,7 +166,7 @@ func (_m *FabricCADB) NamedExec(funcName string, query string, arg interface{}) } // PingContext provides a mock function with given fields: ctx -func (_m *FabricCADB) PingContext(ctx context.Context) error { +func (_m *DbFabricCADB) PingContext(ctx context.Context) error { ret := _m.Called(ctx) var r0 error @@ -168,13 +180,17 @@ func (_m *FabricCADB) PingContext(ctx context.Context) error { } // Queryx provides a mock function with given fields: funcName, query, args -func (_m *FabricCADB) Queryx(funcName string, query string, args ...interface{}) (*sqlx.Rows, error) { +func (_m *DbFabricCADB) Queryx(funcName string, query string, args ...interface{}) (*sqlx.Rows, error) { var _ca []interface{} _ca = append(_ca, funcName, query) _ca = append(_ca, args...) ret := _m.Called(_ca...) var r0 *sqlx.Rows + var r1 error + if rf, ok := ret.Get(0).(func(string, string, ...interface{}) (*sqlx.Rows, error)); ok { + return rf(funcName, query, args...) + } if rf, ok := ret.Get(0).(func(string, string, ...interface{}) *sqlx.Rows); ok { r0 = rf(funcName, query, args...) } else { @@ -183,7 +199,6 @@ func (_m *FabricCADB) Queryx(funcName string, query string, args ...interface{}) } } - var r1 error if rf, ok := ret.Get(1).(func(string, string, ...interface{}) error); ok { r1 = rf(funcName, query, args...) } else { @@ -194,7 +209,7 @@ func (_m *FabricCADB) Queryx(funcName string, query string, args ...interface{}) } // Rebind provides a mock function with given fields: query -func (_m *FabricCADB) Rebind(query string) string { +func (_m *DbFabricCADB) Rebind(query string) string { ret := _m.Called(query) var r0 string @@ -208,7 +223,7 @@ func (_m *FabricCADB) Rebind(query string) string { } // Select provides a mock function with given fields: funcName, dest, query, args -func (_m *FabricCADB) Select(funcName string, dest interface{}, query string, args ...interface{}) error { +func (_m *DbFabricCADB) Select(funcName string, dest interface{}, query string, args ...interface{}) error { var _ca []interface{} _ca = append(_ca, funcName, dest, query) _ca = append(_ca, args...) @@ -225,11 +240,25 @@ func (_m *FabricCADB) Select(funcName string, dest interface{}, query string, ar } // SetDBInitialized provides a mock function with given fields: _a0 -func (_m *FabricCADB) SetDBInitialized(_a0 bool) { +func (_m *DbFabricCADB) SetDBInitialized(_a0 bool) { _m.Called(_a0) } // SetMaxOpenConns provides a mock function with given fields: n -func (_m *FabricCADB) SetMaxOpenConns(n int) { +func (_m *DbFabricCADB) SetMaxOpenConns(n int) { _m.Called(n) } + +// NewDbFabricCADB creates a new instance of DbFabricCADB. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewDbFabricCADB(t interface { + mock.TestingT + Cleanup(func()) +}) *DbFabricCADB { + mock := &DbFabricCADB{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/lib/server/idemix/mocks/my_issuer.go b/lib/server/idemix/mocks/my_issuer.go deleted file mode 100644 index 421166a22..000000000 --- a/lib/server/idemix/mocks/my_issuer.go +++ /dev/null @@ -1,155 +0,0 @@ -// Code generated by mockery v2.7.4. DO NOT EDIT. - -package mocks - -import ( - db "github.com/hyperledger/fabric-ca/lib/server/db" - idemix "github.com/hyperledger/fabric-ca/lib/server/idemix" - - mock "github.com/stretchr/testify/mock" -) - -// MyIssuer is an autogenerated mock type for the MyIssuer type -type MyIssuer struct { - mock.Mock -} - -// Config provides a mock function with given fields: -func (_m *MyIssuer) Config() *idemix.Config { - ret := _m.Called() - - var r0 *idemix.Config - if rf, ok := ret.Get(0).(func() *idemix.Config); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*idemix.Config) - } - } - - return r0 -} - -// CredDBAccessor provides a mock function with given fields: -func (_m *MyIssuer) CredDBAccessor() idemix.CredDBAccessor { - ret := _m.Called() - - var r0 idemix.CredDBAccessor - if rf, ok := ret.Get(0).(func() idemix.CredDBAccessor); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(idemix.CredDBAccessor) - } - } - - return r0 -} - -// DB provides a mock function with given fields: -func (_m *MyIssuer) DB() db.FabricCADB { - ret := _m.Called() - - var r0 db.FabricCADB - if rf, ok := ret.Get(0).(func() db.FabricCADB); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(db.FabricCADB) - } - } - - return r0 -} - -// HomeDir provides a mock function with given fields: -func (_m *MyIssuer) HomeDir() string { - ret := _m.Called() - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// IdemixLib provides a mock function with given fields: -func (_m *MyIssuer) IdemixLib() idemix.Lib { - ret := _m.Called() - - var r0 idemix.Lib - if rf, ok := ret.Get(0).(func() idemix.Lib); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(idemix.Lib) - } - } - - return r0 -} - -// IssuerCredential provides a mock function with given fields: -func (_m *MyIssuer) IssuerCredential() idemix.IssuerCredential { - ret := _m.Called() - - var r0 idemix.IssuerCredential - if rf, ok := ret.Get(0).(func() idemix.IssuerCredential); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(idemix.IssuerCredential) - } - } - - return r0 -} - -// Name provides a mock function with given fields: -func (_m *MyIssuer) Name() string { - ret := _m.Called() - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// NonceManager provides a mock function with given fields: -func (_m *MyIssuer) NonceManager() idemix.NonceManager { - ret := _m.Called() - - var r0 idemix.NonceManager - if rf, ok := ret.Get(0).(func() idemix.NonceManager); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(idemix.NonceManager) - } - } - - return r0 -} - -// RevocationAuthority provides a mock function with given fields: -func (_m *MyIssuer) RevocationAuthority() idemix.RevocationAuthority { - ret := _m.Called() - - var r0 idemix.RevocationAuthority - if rf, ok := ret.Get(0).(func() idemix.RevocationAuthority); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(idemix.RevocationAuthority) - } - } - - return r0 -} diff --git a/lib/server/idemix/mocks/nonce_manager.go b/lib/server/idemix/mocks/nonce_manager.go index 058caa3f8..4c6e9d9d8 100644 --- a/lib/server/idemix/mocks/nonce_manager.go +++ b/lib/server/idemix/mocks/nonce_manager.go @@ -1,11 +1,8 @@ -// Code generated by mockery v2.7.4. DO NOT EDIT. +// Code generated by mockery v2.33.0. DO NOT EDIT. package mocks -import ( - math "github.com/IBM/mathlib" - mock "github.com/stretchr/testify/mock" -) +import mock "github.com/stretchr/testify/mock" // NonceManager is an autogenerated mock type for the NonceManager type type NonceManager struct { @@ -13,11 +10,11 @@ type NonceManager struct { } // CheckNonce provides a mock function with given fields: nonce -func (_m *NonceManager) CheckNonce(nonce *math.Zr) error { +func (_m *NonceManager) CheckNonce(nonce []byte) error { ret := _m.Called(nonce) var r0 error - if rf, ok := ret.Get(0).(func(*math.Zr) error); ok { + if rf, ok := ret.Get(0).(func([]byte) error); ok { r0 = rf(nonce) } else { r0 = ret.Error(0) @@ -27,19 +24,22 @@ func (_m *NonceManager) CheckNonce(nonce *math.Zr) error { } // GetNonce provides a mock function with given fields: -func (_m *NonceManager) GetNonce() (*math.Zr, error) { +func (_m *NonceManager) GetNonce() ([]byte, error) { ret := _m.Called() - var r0 *math.Zr - if rf, ok := ret.Get(0).(func() *math.Zr); ok { + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []byte); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*math.Zr) + r0 = ret.Get(0).([]byte) } } - var r1 error if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { @@ -62,3 +62,17 @@ func (_m *NonceManager) SweepExpiredNonces() error { return r0 } + +// NewNonceManager creates a new instance of NonceManager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewNonceManager(t interface { + mock.TestingT + Cleanup(func()) +}) *NonceManager { + mock := &NonceManager{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/lib/server/idemix/mocks/read_nonce.go b/lib/server/idemix/mocks/read_nonce.go new file mode 100644 index 000000000..c899b7eae --- /dev/null +++ b/lib/server/idemix/mocks/read_nonce.go @@ -0,0 +1,50 @@ +// Code generated by mockery v2.33.0. DO NOT EDIT. + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// ReadNonce is an autogenerated mock type for the ReadNonce type +type ReadNonce struct { + mock.Mock +} + +// Read provides a mock function with given fields: +func (_m *ReadNonce) Read() ([]byte, error) { + ret := _m.Called() + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []byte); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewReadNonce creates a new instance of ReadNonce. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewReadNonce(t interface { + mock.TestingT + Cleanup(func()) +}) *ReadNonce { + mock := &ReadNonce{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/lib/server/idemix/mocks/revocation_authority.go b/lib/server/idemix/mocks/revocation_authority.go new file mode 100644 index 000000000..570f4544b --- /dev/null +++ b/lib/server/idemix/mocks/revocation_authority.go @@ -0,0 +1,117 @@ +// Code generated by mockery v2.33.0. DO NOT EDIT. + +package mocks + +import ( + types "github.com/IBM/idemix/bccsp/types" + mock "github.com/stretchr/testify/mock" +) + +// RevocationAuthority is an autogenerated mock type for the RevocationAuthority type +type RevocationAuthority struct { + mock.Mock +} + +// CreateCRI provides a mock function with given fields: +func (_m *RevocationAuthority) CreateCRI() ([]byte, error) { + ret := _m.Called() + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []byte); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Epoch provides a mock function with given fields: +func (_m *RevocationAuthority) Epoch() (int, error) { + ret := _m.Called() + + var r0 int + var r1 error + if rf, ok := ret.Get(0).(func() (int, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() int); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetNewRevocationHandle provides a mock function with given fields: +func (_m *RevocationAuthority) GetNewRevocationHandle() (int64, error) { + ret := _m.Called() + + var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func() (int64, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() int64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int64) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// PublicKey provides a mock function with given fields: +func (_m *RevocationAuthority) PublicKey() types.Key { + ret := _m.Called() + + var r0 types.Key + if rf, ok := ret.Get(0).(func() types.Key); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(types.Key) + } + } + + return r0 +} + +// NewRevocationAuthority creates a new instance of RevocationAuthority. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewRevocationAuthority(t interface { + mock.TestingT + Cleanup(func()) +}) *RevocationAuthority { + mock := &RevocationAuthority{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/lib/server/idemix/mocks/server_request_ctx.go b/lib/server/idemix/mocks/server_request_ctx.go index 0ad2a92ea..93ec44682 100644 --- a/lib/server/idemix/mocks/server_request_ctx.go +++ b/lib/server/idemix/mocks/server_request_ctx.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.7.4. DO NOT EDIT. +// Code generated by mockery v2.33.0. DO NOT EDIT. package mocks @@ -17,13 +17,16 @@ func (_m *ServerRequestCtx) BasicAuthentication() (string, error) { ret := _m.Called() var r0 string + var r1 error + if rf, ok := ret.Get(0).(func() (string, error)); ok { + return rf() + } if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() } else { r0 = ret.Get(0).(string) } - var r1 error if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { @@ -38,6 +41,10 @@ func (_m *ServerRequestCtx) GetCaller() (user.User, error) { ret := _m.Called() var r0 user.User + var r1 error + if rf, ok := ret.Get(0).(func() (user.User, error)); ok { + return rf() + } if rf, ok := ret.Get(0).(func() user.User); ok { r0 = rf() } else { @@ -46,7 +53,6 @@ func (_m *ServerRequestCtx) GetCaller() (user.User, error) { } } - var r1 error if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { @@ -89,13 +95,16 @@ func (_m *ServerRequestCtx) TokenAuthentication() (string, error) { ret := _m.Called() var r0 string + var r1 error + if rf, ok := ret.Get(0).(func() (string, error)); ok { + return rf() + } if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() } else { r0 = ret.Get(0).(string) } - var r1 error if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { @@ -104,3 +113,17 @@ func (_m *ServerRequestCtx) TokenAuthentication() (string, error) { return r0, r1 } + +// NewServerRequestCtx creates a new instance of ServerRequestCtx. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewServerRequestCtx(t interface { + mock.TestingT + Cleanup(func()) +}) *ServerRequestCtx { + mock := &ServerRequestCtx{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/lib/server/idemix/mocks/sql_result.go b/lib/server/idemix/mocks/sql_result.go new file mode 100644 index 000000000..f4185458c --- /dev/null +++ b/lib/server/idemix/mocks/sql_result.go @@ -0,0 +1,72 @@ +// Code generated by mockery v2.33.0. DO NOT EDIT. + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// SqlResult is an autogenerated mock type for the SqlResult type +type SqlResult struct { + mock.Mock +} + +// LastInsertId provides a mock function with given fields: +func (_m *SqlResult) LastInsertId() (int64, error) { + ret := _m.Called() + + var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func() (int64, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() int64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int64) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RowsAffected provides a mock function with given fields: +func (_m *SqlResult) RowsAffected() (int64, error) { + ret := _m.Called() + + var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func() (int64, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() int64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int64) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewSqlResult creates a new instance of SqlResult. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewSqlResult(t interface { + mock.TestingT + Cleanup(func()) +}) *SqlResult { + mock := &SqlResult{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/lib/server/idemix/mocks/User.go b/lib/server/idemix/mocks/user_user.go similarity index 69% rename from lib/server/idemix/mocks/User.go rename to lib/server/idemix/mocks/user_user.go index aabdfb3f4..eeafc123d 100644 --- a/lib/server/idemix/mocks/User.go +++ b/lib/server/idemix/mocks/user_user.go @@ -1,16 +1,20 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. +// Code generated by mockery v2.33.0. DO NOT EDIT. + package mocks -import api "github.com/hyperledger/fabric-ca/api" -import mock "github.com/stretchr/testify/mock" +import ( + api "github.com/hyperledger/fabric-ca/api" + + mock "github.com/stretchr/testify/mock" +) -// User is an autogenerated mock type for the User type -type User struct { +// UserUser is an autogenerated mock type for the UserUser type +type UserUser struct { mock.Mock } // GetAffiliationPath provides a mock function with given fields: -func (_m *User) GetAffiliationPath() []string { +func (_m *UserUser) GetAffiliationPath() []string { ret := _m.Called() var r0 []string @@ -26,10 +30,14 @@ func (_m *User) GetAffiliationPath() []string { } // GetAttribute provides a mock function with given fields: name -func (_m *User) GetAttribute(name string) (*api.Attribute, error) { +func (_m *UserUser) GetAttribute(name string) (*api.Attribute, error) { ret := _m.Called(name) var r0 *api.Attribute + var r1 error + if rf, ok := ret.Get(0).(func(string) (*api.Attribute, error)); ok { + return rf(name) + } if rf, ok := ret.Get(0).(func(string) *api.Attribute); ok { r0 = rf(name) } else { @@ -38,7 +46,6 @@ func (_m *User) GetAttribute(name string) (*api.Attribute, error) { } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(name) } else { @@ -49,10 +56,14 @@ func (_m *User) GetAttribute(name string) (*api.Attribute, error) { } // GetAttributes provides a mock function with given fields: attrNames -func (_m *User) GetAttributes(attrNames []string) ([]api.Attribute, error) { +func (_m *UserUser) GetAttributes(attrNames []string) ([]api.Attribute, error) { ret := _m.Called(attrNames) var r0 []api.Attribute + var r1 error + if rf, ok := ret.Get(0).(func([]string) ([]api.Attribute, error)); ok { + return rf(attrNames) + } if rf, ok := ret.Get(0).(func([]string) []api.Attribute); ok { r0 = rf(attrNames) } else { @@ -61,7 +72,6 @@ func (_m *User) GetAttributes(attrNames []string) ([]api.Attribute, error) { } } - var r1 error if rf, ok := ret.Get(1).(func([]string) error); ok { r1 = rf(attrNames) } else { @@ -72,7 +82,7 @@ func (_m *User) GetAttributes(attrNames []string) ([]api.Attribute, error) { } // GetFailedLoginAttempts provides a mock function with given fields: -func (_m *User) GetFailedLoginAttempts() int { +func (_m *UserUser) GetFailedLoginAttempts() int { ret := _m.Called() var r0 int @@ -86,7 +96,7 @@ func (_m *User) GetFailedLoginAttempts() int { } // GetLevel provides a mock function with given fields: -func (_m *User) GetLevel() int { +func (_m *UserUser) GetLevel() int { ret := _m.Called() var r0 int @@ -100,7 +110,7 @@ func (_m *User) GetLevel() int { } // GetMaxEnrollments provides a mock function with given fields: -func (_m *User) GetMaxEnrollments() int { +func (_m *UserUser) GetMaxEnrollments() int { ret := _m.Called() var r0 int @@ -114,7 +124,7 @@ func (_m *User) GetMaxEnrollments() int { } // GetName provides a mock function with given fields: -func (_m *User) GetName() string { +func (_m *UserUser) GetName() string { ret := _m.Called() var r0 string @@ -128,7 +138,7 @@ func (_m *User) GetName() string { } // GetType provides a mock function with given fields: -func (_m *User) GetType() string { +func (_m *UserUser) GetType() string { ret := _m.Called() var r0 string @@ -142,7 +152,7 @@ func (_m *User) GetType() string { } // IncrementIncorrectPasswordAttempts provides a mock function with given fields: -func (_m *User) IncrementIncorrectPasswordAttempts() error { +func (_m *UserUser) IncrementIncorrectPasswordAttempts() error { ret := _m.Called() var r0 error @@ -156,7 +166,7 @@ func (_m *User) IncrementIncorrectPasswordAttempts() error { } // IsRevoked provides a mock function with given fields: -func (_m *User) IsRevoked() bool { +func (_m *UserUser) IsRevoked() bool { ret := _m.Called() var r0 bool @@ -170,7 +180,7 @@ func (_m *User) IsRevoked() bool { } // Login provides a mock function with given fields: password, caMaxEnrollment -func (_m *User) Login(password string, caMaxEnrollment int) error { +func (_m *UserUser) Login(password string, caMaxEnrollment int) error { ret := _m.Called(password, caMaxEnrollment) var r0 error @@ -184,7 +194,7 @@ func (_m *User) Login(password string, caMaxEnrollment int) error { } // LoginComplete provides a mock function with given fields: -func (_m *User) LoginComplete() error { +func (_m *UserUser) LoginComplete() error { ret := _m.Called() var r0 error @@ -198,7 +208,7 @@ func (_m *User) LoginComplete() error { } // ModifyAttributes provides a mock function with given fields: attrs -func (_m *User) ModifyAttributes(attrs []api.Attribute) error { +func (_m *UserUser) ModifyAttributes(attrs []api.Attribute) error { ret := _m.Called(attrs) var r0 error @@ -212,7 +222,7 @@ func (_m *User) ModifyAttributes(attrs []api.Attribute) error { } // Revoke provides a mock function with given fields: -func (_m *User) Revoke() error { +func (_m *UserUser) Revoke() error { ret := _m.Called() var r0 error @@ -226,7 +236,7 @@ func (_m *User) Revoke() error { } // SetLevel provides a mock function with given fields: level -func (_m *User) SetLevel(level int) error { +func (_m *UserUser) SetLevel(level int) error { ret := _m.Called(level) var r0 error @@ -238,3 +248,17 @@ func (_m *User) SetLevel(level int) error { return r0 } + +// NewUserUser creates a new instance of UserUser. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUserUser(t interface { + mock.TestingT + Cleanup(func()) +}) *UserUser { + mock := &UserUser{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/lib/server/idemix/nonce.go b/lib/server/idemix/nonce.go index c9bb78e19..31d4ff362 100644 --- a/lib/server/idemix/nonce.go +++ b/lib/server/idemix/nonce.go @@ -7,10 +7,10 @@ SPDX-License-Identifier: Apache-2.0 package idemix import ( + "crypto/rand" "fmt" "time" - math "github.com/IBM/mathlib" "github.com/cloudflare/cfssl/log" "github.com/hyperledger/fabric-ca/lib/server/db" "github.com/hyperledger/fabric-ca/util" @@ -39,62 +39,85 @@ type Nonce struct { Level int `db:"level"` } +//go:generate mockery --name NonceManager --case underscore + // NonceManager represents nonce manager that is responsible for // getting a new nonce type NonceManager interface { // GetNonce creates a nonce, stores it in the database and returns it - GetNonce() (*math.Zr, error) + GetNonce() ([]byte, error) // CheckNonce checks if the specified nonce exists in the database and has not expired - CheckNonce(nonce *math.Zr) error + CheckNonce(nonce []byte) error // SweepExpiredNonces removes expired nonces from the database SweepExpiredNonces() error } +//go:generate mockery --name ReadNonce --case underscore +type ReadNonce interface { + Read() ([]byte, error) +} + // Clock provides time related functions +// +//go:generate mockery --name Clock --case underscore type Clock interface { Now() time.Time } -// nonceManager implements NonceManager interface -type nonceManager struct { +// NonceManagerImpl implements NonceManager interface +type NonceManagerImpl struct { nonceExpiration time.Duration nonceSweepInterval time.Duration clock Clock - issuer MyIssuer + issuer *IssuerInst level int + ReadNonce ReadNonce +} + +type readNonceImpl struct { +} + +func (*readNonceImpl) Read() ([]byte, error) { + nonceBytes := make([]byte, 32) + _, err := rand.Read(nonceBytes) + if err != nil { + fmt.Println(">>>>>>>>>>>>>>>>>>>") + return nil, err + } + + return nonceBytes, err } // NewNonceManager returns an instance of an object that implements NonceManager interface -func NewNonceManager(issuer MyIssuer, clock Clock, level int) (NonceManager, error) { +func NewNonceManager(issuer *IssuerInst, clock Clock, level int) (NonceManager, error) { var err error - mgr := &nonceManager{ + mgr := &NonceManagerImpl{ issuer: issuer, clock: clock, level: level, } - opts := issuer.Config() + opts := issuer.Cfg mgr.nonceExpiration, err = time.ParseDuration(opts.NonceExpiration) if err != nil { return nil, errors.Wrapf(err, fmt.Sprintf("Failed to parse idemix.nonceexpiration config option while initializing Nonce manager for Issuer '%s'", - issuer.Name())) + issuer.Name)) } mgr.nonceSweepInterval, err = time.ParseDuration(opts.NonceSweepInterval) if err != nil { return nil, errors.Wrapf(err, fmt.Sprintf("Failed to parse idemix.noncesweepinterval config option while initializing Nonce manager for Issuer '%s'", - issuer.Name())) + issuer.Name)) } + mgr.ReadNonce = &readNonceImpl{} return mgr, nil } // GetNonce returns a new nonce -func (nm *nonceManager) GetNonce() (*math.Zr, error) { - idmixLib := nm.issuer.IdemixLib() - nonce, err := idmixLib.RandModOrder() +func (nm *NonceManagerImpl) GetNonce() ([]byte, error) { + nonceBytes, err := nm.ReadNonce.Read() if err != nil { - fmt.Println(">>>>>>>>>>>>>>>>>>>") return nil, err } - nonceBytes := nonce.Bytes() + err = nm.insertNonceInDB(&Nonce{ Val: util.B64Encode(nonceBytes), Expiry: nm.clock.Now().UTC().Add(nm.nonceExpiration), @@ -104,15 +127,14 @@ func (nm *nonceManager) GetNonce() (*math.Zr, error) { log.Errorf("Failed to store nonce: %s", err.Error()) return nil, errors.WithMessage(err, "Failed to store nonce") } - return nonce, nil + return nonceBytes, nil } // CheckNonce checks if the specified nonce is valid (is in DB and has not expired) // and the nonce is removed from DB -func (nm *nonceManager) CheckNonce(nonce *math.Zr) error { - nonceBytes := nonce.Bytes() +func (nm *NonceManagerImpl) CheckNonce(nonceBytes []byte) error { queryParam := util.B64Encode(nonceBytes) - nonceRec, err := doTransaction("CheckNonce", nm.issuer.DB(), nm.getNonceFromDB, queryParam) + nonceRec, err := doTransaction("CheckNonce", nm.issuer.Db, nm.getNonceFromDB, queryParam) if err != nil { return err } @@ -126,14 +148,14 @@ func (nm *nonceManager) CheckNonce(nonce *math.Zr) error { } // SweepExpiredNonces sweeps expired nonces -func (nm *nonceManager) SweepExpiredNonces() error { +func (nm *NonceManagerImpl) SweepExpiredNonces() error { return nm.sweep(nm.clock.Now().UTC()) } // StartNonceSweeper starts a separate thread that will remove expired // nonces at the interval specified by the idemix.noncesweepinterval. This // function should be called while initializing the server. -func (nm *nonceManager) StartNonceSweeper() { +func (nm *NonceManagerImpl) StartNonceSweeper() { go func() { ticker := time.NewTicker(nm.nonceSweepInterval) for t := range ticker.C { @@ -143,13 +165,13 @@ func (nm *nonceManager) StartNonceSweeper() { } // sweep deletes all nonces that have expired (whose expiry is less than current timestamp) -func (nm *nonceManager) sweep(curTime time.Time) error { - log.Debugf("Cleaning up expired nonces for CA '%s'", nm.issuer.Name()) +func (nm *NonceManagerImpl) sweep(curTime time.Time) error { + log.Debugf("Cleaning up expired nonces for CA '%s'", nm.issuer.Name) return nm.removeExpiredNoncesFromDB(curTime) } // Gets the specified nonce from DB and removes it from the DB -func (nm *nonceManager) getNonceFromDB(tx db.FabricCATx, args ...interface{}) (interface{}, error) { +func (nm *NonceManagerImpl) getNonceFromDB(tx db.FabricCATx, args ...interface{}) (interface{}, error) { nonces := []Nonce{} err := tx.Select("GetNonce", &nonces, tx.Rebind(SelectNonce), args...) if err != nil { @@ -171,17 +193,17 @@ func (nm *nonceManager) getNonceFromDB(tx db.FabricCATx, args ...interface{}) (i return nonces[0], nil } -func (nm *nonceManager) removeExpiredNoncesFromDB(curTime time.Time) error { - _, err := nm.issuer.DB().Exec("RemoveExpiredNonces", nm.issuer.DB().Rebind(RemoveExpiredNonces), curTime) +func (nm *NonceManagerImpl) removeExpiredNoncesFromDB(curTime time.Time) error { + _, err := nm.issuer.Db.Exec("RemoveExpiredNonces", nm.issuer.Db.Rebind(RemoveExpiredNonces), curTime) if err != nil { - log.Errorf("Failed to remove expired nonces from DB for CA '%s': %s", nm.issuer.Name(), err.Error()) + log.Errorf("Failed to remove expired nonces from DB for CA '%s': %s", nm.issuer.Name, err.Error()) return errors.New("Failed to remove expired nonces from DB") } return nil } -func (nm *nonceManager) insertNonceInDB(nonce *Nonce) error { - res, err := nm.issuer.DB().NamedExec("InsertNonce", InsertNonce, nonce) +func (nm *NonceManagerImpl) insertNonceInDB(nonce *Nonce) error { + res, err := nm.issuer.Db.NamedExec("InsertNonce", InsertNonce, nonce) if err != nil { log.Errorf("Failed to add nonce to DB: %s", err.Error()) return errors.New("Failed to add nonce to the datastore") diff --git a/lib/server/idemix/nonce_test.go b/lib/server/idemix/nonce_test.go index cabdecd98..51383f8b2 100644 --- a/lib/server/idemix/nonce_test.go +++ b/lib/server/idemix/nonce_test.go @@ -9,31 +9,25 @@ package idemix_test import ( "crypto/rand" "database/sql" - "fmt" "testing" "time" - "github.com/hyperledger/fabric-amcl/amcl" - idmx "github.com/hyperledger/fabric-ca/lib/common/idemix" . "github.com/hyperledger/fabric-ca/lib/server/idemix" "github.com/hyperledger/fabric-ca/lib/server/idemix/mocks" - dmocks "github.com/hyperledger/fabric-ca/lib/server/idemix/mocks" "github.com/hyperledger/fabric-ca/util" "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) func TestNewNonceManager(t *testing.T) { - issuer := new(mocks.MyIssuer) - issuer.On("Name").Return("ca1") + issuer := new(IssuerInst) + issuer.Name = "ca1" opts := &Config{ NonceExpiration: "15", NonceSweepInterval: "15", } clock := new(mocks.Clock) - lib := new(mocks.Lib) - issuer.On("Config").Return(opts) - issuer.On("IdemixLib").Return(lib) + issuer.Cfg = opts _, err := NewNonceManager(issuer, clock, 1) assert.Error(t, err, "NewNonceManager should return error if the NonceExpiration config option is not in time.Duration string format") assert.Contains(t, err.Error(), "Failed to parse idemix.nonceexpiration config option while initializing Nonce manager for Issuer 'ca1'") @@ -49,33 +43,14 @@ func TestNewNonceManager(t *testing.T) { } func TestGetNonce(t *testing.T) { - for _, curve := range idmx.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testCheckNonce(t, curve) - }) - } -} - -func testGetNonce(t *testing.T, curveID idmx.CurveID) { - issuer := new(mocks.MyIssuer) - issuer.On("Name").Return("ca1") - - curve := idmx.CurveByID(curveID) - - lib := new(mocks.Lib) - rnd, err := curve.Rand() - if err != nil { - t.Fatalf("Error generating a random number") - } + issuer := new(IssuerInst) + issuer.Name = "ca1" - rmo := curve.NewRandomZr(rnd) - rmo.Mod(curve.GroupOrder) - - lib.On("RandModOrder", rnd).Return(rmo, nil) - - issuer.On("IdemixRand").Return(rnd) + nonceBytes := make([]byte, 32) + _, err := rand.Read(nonceBytes) + assert.NoError(t, err) - noncestr := util.B64Encode(rmo.Bytes()) + noncestr := util.B64Encode(nonceBytes) now := time.Now() nonceObj := &Nonce{ Val: noncestr, @@ -85,16 +60,16 @@ func testGetNonce(t *testing.T, curveID idmx.CurveID) { numResultForRowsAffectedCalls := 0 f1 := getResultForRowsAffectedFunc(&numResultForRowsAffectedCalls) - result := new(dmocks.Result) + result := new(mocks.SqlResult) result.On("RowsAffected").Return(f1, nil) - db := new(dmocks.FabricCADB) + db := new(mocks.DbFabricCADB) numResultForInsertNonceCalls := 0 numErrorForInsertNonceCalls := 0 f2 := getResultForInsertNonceFunc(result, &numResultForInsertNonceCalls) f3 := getErrorForInsertNonceFunc(result, &numErrorForInsertNonceCalls) db.On("NamedExec", "InsertNonce", InsertNonce, nonceObj).Return(f2, f3) - issuer.On("DB").Return(db) + issuer.Db = db opts := &Config{ NonceExpiration: "15s", @@ -102,10 +77,13 @@ func testGetNonce(t *testing.T, curveID idmx.CurveID) { } clock := new(mocks.Clock) clock.On("Now").Return(now) - issuer.On("Config").Return(opts) - issuer.On("IdemixLib").Return(lib) + issuer.Cfg = opts nm, err := NewNonceManager(issuer, clock, 1) + mockRand := new(mocks.ReadNonce) + nm.(*NonceManagerImpl).ReadNonce = mockRand + mockRand.On("Read").Return(nonceBytes, nil) + _, err = nm.GetNonce() assert.Error(t, err, "Executing insert SQL should return an error") if err != nil { @@ -129,40 +107,17 @@ func testGetNonce(t *testing.T, curveID idmx.CurveID) { } func TestCheckNonce(t *testing.T) { - for _, curve := range idmx.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testCheckNonce(t, curve) - }) - } -} - -func testCheckNonce(t *testing.T, curveID idmx.CurveID) { - issuer := new(mocks.MyIssuer) - issuer.On("Name").Return("ca1") + issuer := new(IssuerInst) + issuer.Name = "ca1" - lib := new(mocks.Lib) - rnd, err := getRand() - if err != nil { - t.Fatalf("Error generating a random number") - } - - curve := idmx.CurveByID(curveID) - rand, err := curve.Rand() - if err != nil { - t.Fatalf("Error obtaining randomness source") - } - - rmo := curve.NewRandomZr(rand) - rmo.Mod(curve.GroupOrder) - - lib.On("RandModOrder", rnd).Return(rmo) + nonceBytes := make([]byte, 32) + _, err := rand.Read(nonceBytes) + assert.NoError(t, err) - issuer.On("IdemixRand").Return(rnd) - issuer.On("GetIdemixLib").Return(lib) - noncestr := util.B64Encode(rmo.Bytes()) + noncestr := util.B64Encode(nonceBytes) - db := new(dmocks.FabricCADB) - tx := new(dmocks.FabricCATx) + db := new(mocks.DbFabricCADB) + tx := new(mocks.DbFabricCATx) tx.On("Commit", "CheckNonce").Return(nil) tx.On("Rollback", "CheckNonce").Return(nil) nonces := []Nonce{} @@ -177,13 +132,13 @@ func testCheckNonce(t *testing.T, curveID idmx.CurveID) { f1 := getTxRemoveNonceResultFunc(noncestr, &numTxRemoveResultCalls) f2 := getTxRemoveNonceErrorFunc(&numTxRemoveErrorCalls) tx.On("Exec", "GetNonce", RemoveNonce, noncestr).Return(f1, f2) - issuer.On("DB").Return(db) + issuer.Db = db opts := &Config{ NonceExpiration: "15s", NonceSweepInterval: "15m", } - issuer.On("Config").Return(opts) + issuer.Cfg = opts now := time.Now() clock := new(mocks.Clock) clock.On("Now").Return(now) @@ -191,52 +146,42 @@ func testCheckNonce(t *testing.T, curveID idmx.CurveID) { if err != nil { t.Fatalf("Failed to get new instance of Nonce Manager") } - err = nm.CheckNonce(rmo) + err = nm.CheckNonce(nonceBytes) assert.Error(t, err) assert.Contains(t, err.Error(), "Failed to retrieve nonce from the datastore") - err = nm.CheckNonce(rmo) + err = nm.CheckNonce(nonceBytes) assert.Error(t, err) assert.Contains(t, err.Error(), "Nonce not found in the datastore") - err = nm.CheckNonce(rmo) + err = nm.CheckNonce(nonceBytes) assert.Error(t, err) assert.Contains(t, err.Error(), "Nonce is either unknown or has expired") - err = nm.CheckNonce(rmo) + err = nm.CheckNonce(nonceBytes) assert.NoError(t, err) - err = nm.CheckNonce(rmo) + err = nm.CheckNonce(nonceBytes) assert.NoError(t, err) } func TestSweepExpiredNonces(t *testing.T) { - for _, curve := range idmx.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testSweepExpiredNonces(t, curve) - }) - } -} - -func testSweepExpiredNonces(t *testing.T, curveID idmx.CurveID) { - issuer := new(mocks.MyIssuer) - issuer.On("Name").Return("ca1") + issuer := new(IssuerInst) + issuer.Name = "ca1" now := time.Now() numRemoveExpiredNoncesErrorFuncCalls := 0 f := getRemoveExpiredNoncesErrorFunc(&numRemoveExpiredNoncesErrorFuncCalls) - db := new(dmocks.FabricCADB) + db := new(mocks.DbFabricCADB) db.On("Rebind", RemoveExpiredNonces).Return(RemoveExpiredNonces) db.On("Exec", "RemoveExpiredNonces", RemoveExpiredNonces, now.UTC()).Return(nil, f) // errors.New("error")) - issuer.On("DB").Return(db) + issuer.Db = db - lib := new(mocks.Lib) opts := &Config{ NonceExpiration: "15s", NonceSweepInterval: "15m", } - issuer.On("Config").Return(opts) - issuer.On("IdemixLib").Return(lib) + issuer.Cfg = opts clock := new(mocks.Clock) clock.On("Now").Return(now) nm, err := NewNonceManager(issuer, clock, 1) @@ -320,7 +265,7 @@ func getTxRemoveNonceResultFunc(noncestr string, numTxRemoveResultCalls *int) fu *numTxRemoveResultCalls = *numTxRemoveResultCalls + 1 return nil } - result := new(dmocks.Result) + result := new(mocks.SqlResult) if *numTxRemoveResultCalls == 1 { result.On("RowsAffected").Return(int64(2), nil) *numTxRemoveResultCalls = *numTxRemoveResultCalls + 1 @@ -350,17 +295,3 @@ func getRemoveExpiredNoncesErrorFunc(numRemoveExpiredNoncesErrorFuncCalls *int) return nil } } - -// getRand returns a new *amcl.RAND with a fresh seed -func getRand() (*amcl.RAND, error) { - seedLength := 32 - b := make([]byte, seedLength) - _, err := rand.Read(b) - if err != nil { - return nil, errors.Wrap(err, "error getting randomness for seed") - } - rng := amcl.NewRAND() - rng.Clean() - rng.Seed(seedLength, b) - return rng, nil -} diff --git a/lib/server/idemix/ra_whitebox_test.go b/lib/server/idemix/ra_whitebox_test.go index fe55c392f..80c3476fa 100644 --- a/lib/server/idemix/ra_whitebox_test.go +++ b/lib/server/idemix/ra_whitebox_test.go @@ -7,25 +7,17 @@ SPDX-License-Identifier: Apache-2.0 package idemix import ( - "fmt" + "math/big" "testing" - idemix "github.com/hyperledger/fabric-ca/lib/common/idemix" + "github.com/IBM/mathlib/driver/common" "github.com/hyperledger/fabric-ca/lib/server/db" "github.com/hyperledger/fabric-ca/util" "github.com/stretchr/testify/assert" ) func TestGetUnRevokedHandles(t *testing.T) { - for _, curve := range idemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testGetUnRevokedHandles(t, curve) - }) - } -} - -func testGetUnRevokedHandles(t *testing.T, curveID idemix.CurveID) { - ra := &revocationAuthority{issuer: &issuer{name: "ca1", homeDir: ".", cfg: &Config{}}, curve: idemix.CurveByID(curveID)} + ra := &revocationAuthority{issuer: &IssuerInst{Name: "ca1", HomeDir: ".", Cfg: &Config{}}} info := &RevocationAuthorityInfo{ Epoch: 1, LastHandleInPool: 100, @@ -40,7 +32,7 @@ func testGetUnRevokedHandles(t *testing.T, curveID idemix.CurveID) { assert.Equal(t, 100, len(unrevokedHandles)) revokedCred = CredRecord{ - RevocationHandle: util.B64Encode(idemix.CurveByID(curveID).NewZrFromInt(10).Bytes()), + RevocationHandle: util.B64Encode(common.BigToBytes(big.NewInt(int64(10)))), } revokedCreds = []CredRecord{revokedCred} unrevokedHandles = ra.getUnRevokedHandles(info, revokedCreds) diff --git a/lib/server/idemix/revocationauthority.go b/lib/server/idemix/revocationauthority.go index 985bfda44..cee74c554 100644 --- a/lib/server/idemix/revocationauthority.go +++ b/lib/server/idemix/revocationauthority.go @@ -8,13 +8,13 @@ package idemix import ( "bytes" - "crypto/ecdsa" "fmt" + "math/big" - idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" - math "github.com/IBM/mathlib" + "github.com/IBM/idemix/bccsp/types" + ibccsp "github.com/IBM/idemix/bccsp/types" + "github.com/IBM/mathlib/driver/common" "github.com/cloudflare/cfssl/log" - cidemix "github.com/hyperledger/fabric-ca/lib/common/idemix" "github.com/hyperledger/fabric-ca/lib/server/db" "github.com/hyperledger/fabric-ca/util" "github.com/jmoiron/sqlx" @@ -34,23 +34,25 @@ const ( DefaultRevocationHandlePoolSize = 1000 ) +//go:generate mockery --name RevocationAuthority --case underscore + // RevocationAuthority is responsible for generating revocation handles and // credential revocation info (CRI) type RevocationAuthority interface { // GetNewRevocationHandle returns new revocation handle, which is required to // create a new Idemix credential - GetNewRevocationHandle() (*math.Zr, error) + GetNewRevocationHandle() (int64, error) // CreateCRI returns latest credential revocation information (CRI). CRI contains // information that allows a prover to create a proof that the revocation handle associated // with his credential is not revoked and by the verifier to verify the non-revocation // proof of the prover. Verification will fail if the version of the CRI that verifier has // does not match the version of the CRI that prover used to create non-revocation proof. // The version of the CRI is specified by the Epoch value associated with the CRI. - CreateCRI() (*idemix.CredentialRevocationInformation, error) + CreateCRI() ([]byte, error) // Epoch returns epoch value of the latest CRI Epoch() (int, error) // PublicKey returns revocation authority's public key - PublicKey() *ecdsa.PublicKey + PublicKey() ibccsp.Key } // RevocationAuthorityInfo is the revocation authority information record that is @@ -64,19 +66,18 @@ type RevocationAuthorityInfo struct { // revocationAuthority implements RevocationComponent interface type revocationAuthority struct { - curve *math.Curve - issuer MyIssuer - key RevocationKey - db db.FabricCADB - currentCRI *idemix.CredentialRevocationInformation + issuer *IssuerInst + key RevocationKey + db db.FabricCADB + currentCRI []byte + currentEpoch int64 } // NewRevocationAuthority constructor for revocation authority -func NewRevocationAuthority(issuer MyIssuer, level int, curveID cidemix.CurveID) (RevocationAuthority, error) { +func NewRevocationAuthority(issuer *IssuerInst, level int) (RevocationAuthority, error) { ra := &revocationAuthority{ - curve: cidemix.CurveByID(curveID), issuer: issuer, - db: issuer.DB(), + db: issuer.Db, } var err error @@ -88,7 +89,7 @@ func NewRevocationAuthority(issuer MyIssuer, level int, curveID cidemix.CurveID) info, err := ra.getRAInfoFromDB() if err != nil { return nil, errors.WithMessage(err, - fmt.Sprintf("Failed to initialize revocation authority for issuer '%s'", issuer.Name())) + fmt.Sprintf("Failed to initialize revocation authority for issuer '%s'", issuer.Name)) } // If epoch is 0, it means this is the first time revocation authority is being @@ -97,13 +98,13 @@ func NewRevocationAuthority(issuer MyIssuer, level int, curveID cidemix.CurveID) rcInfo := RevocationAuthorityInfo{ Epoch: 1, NextRevocationHandle: 1, - LastHandleInPool: issuer.Config().RHPoolSize, + LastHandleInPool: issuer.Cfg.RHPoolSize, Level: level, } err = ra.addRAInfoToDB(&rcInfo) if err != nil { return nil, errors.WithMessage(err, - fmt.Sprintf("Failed to initialize revocation authority for issuer '%s'", issuer.Name())) + fmt.Sprintf("Failed to initialize revocation authority for issuer '%s'", issuer.Name)) } info = &rcInfo } @@ -113,9 +114,9 @@ func NewRevocationAuthority(issuer MyIssuer, level int, curveID cidemix.CurveID) func (ra *revocationAuthority) initKeyMaterial(renew bool) error { log.Debug("Initialize Idemix issuer revocation key material") - revocationPubKey := ra.issuer.Config().RevocationPublicKeyfile - revocationPrivKey := ra.issuer.Config().RevocationPrivateKeyfile - rk := NewRevocationKey(revocationPubKey, revocationPrivKey, ra.issuer.IdemixLib()) + revocationPubKey := ra.issuer.Cfg.RevocationPublicKeyfile + revocationPrivKey := ra.issuer.Cfg.RevocationPrivateKeyfile + rk := NewRevocationKey(revocationPubKey, revocationPrivKey, ra.issuer.Csp) if !renew { pubKeyFileExists := util.FileExists(revocationPubKey) @@ -127,7 +128,7 @@ func (ra *revocationAuthority) initKeyMaterial(renew bool) error { log.Infof(" public key file location: %s", revocationPubKey) err := rk.Load() if err != nil { - return errors.WithMessage(err, fmt.Sprintf("Failed to load revocation key for issuer '%s'", ra.issuer.Name())) + return errors.WithMessage(err, fmt.Sprintf("Failed to load revocation key for issuer '%s'", ra.issuer.Name)) } ra.key = rk return nil @@ -136,12 +137,12 @@ func (ra *revocationAuthority) initKeyMaterial(renew bool) error { err := rk.SetNewKey() if err != nil { return errors.WithMessage(err, - fmt.Sprintf("Failed to generate revocation key for issuer '%s'", ra.issuer.Name())) + fmt.Sprintf("Failed to generate revocation key for issuer '%s'", ra.issuer.Name)) } - log.Infof("Idemix issuer revocation public and secret keys were generated for CA '%s'", ra.issuer.Name()) + log.Infof("Idemix issuer revocation public and secret keys were generated for CA '%s'", ra.issuer.Name) err = rk.Store() if err != nil { - return errors.WithMessage(err, fmt.Sprintf("Failed to store revocation key of issuer '%s'", ra.issuer.Name())) + return errors.WithMessage(err, fmt.Sprintf("Failed to store revocation key of issuer '%s'", ra.issuer.Name)) } ra.key = rk return nil @@ -153,39 +154,48 @@ func (ra *revocationAuthority) initKeyMaterial(renew bool) error { // proof of the prover. Verification will fail if the version of the CRI that verifier has // does not match the version of the CRI that prover used to create non-revocation proof. // The version of the CRI is specified by the Epoch value associated with the CRI. -func (ra *revocationAuthority) CreateCRI() (*idemix.CredentialRevocationInformation, error) { +func (ra *revocationAuthority) CreateCRI() ([]byte, error) { info, err := ra.getRAInfoFromDB() if err != nil { return nil, errors.WithMessage(err, "Failed to get revocation authority info from datastore") } - if ra.currentCRI != nil && ra.currentCRI.Epoch == int64(info.Epoch) { + if ra.currentCRI != nil && ra.currentEpoch == int64(info.Epoch) { return ra.currentCRI, nil } - revokedCreds, err := ra.issuer.CredDBAccessor().GetRevokedCredentials() + revokedCreds, err := ra.issuer.CredDBAccessor.GetRevokedCredentials() if err != nil { - return nil, errors.WithMessage(err, fmt.Sprintf("Failed to get revoked credentials while generating CRI for issuer: '%s'", ra.issuer.Name())) + return nil, errors.WithMessage(err, fmt.Sprintf("Failed to get revoked credentials while generating CRI for issuer: '%s'", ra.issuer.Name)) } unrevokedHandles := ra.getUnRevokedHandles(info, revokedCreds) - alg := idemix.ALG_NO_REVOCATION - cri, err := ra.issuer.IdemixLib().CreateCRI(ra.key.GetKey(), unrevokedHandles, info.Epoch, alg) + cri, err := ra.issuer.Csp.Sign( + ra.key.GetKey(), + nil, + &ibccsp.IdemixCRISignerOpts{ + UnrevokedHandles: unrevokedHandles, + Epoch: info.Epoch, + RevocationAlgorithm: types.AlgNoRevocation, + }, + ) if err != nil { return nil, err } + ra.currentCRI = cri + ra.currentEpoch = int64(info.Epoch) return ra.currentCRI, nil } // GetNewRevocationHandle returns a new revocation handle -func (ra *revocationAuthority) GetNewRevocationHandle() (*math.Zr, error) { +func (ra *revocationAuthority) GetNewRevocationHandle() (int64, error) { h, err := ra.getNextRevocationHandle() if err != nil { - return nil, err + return -1, err } - return ra.curve.NewZrFromInt(int64(h)), nil + return int64(h), nil } // Epoch returns epoch value of the latest CRI @@ -198,28 +208,28 @@ func (ra *revocationAuthority) Epoch() (int, error) { } // PublicKey returns revocation authority's public key -func (ra *revocationAuthority) PublicKey() *ecdsa.PublicKey { - return &ra.key.GetKey().PublicKey +func (ra *revocationAuthority) PublicKey() ibccsp.Key { + return ra.key.GetKey() } -func (ra *revocationAuthority) getUnRevokedHandles(info *RevocationAuthorityInfo, revokedCreds []CredRecord) []*math.Zr { - log.Debugf("RA '%s' is getting revoked revocation handles for epoch %d", ra.issuer.Name(), info.Epoch) - isRevokedHandle := func(rh *math.Zr) bool { +func (ra *revocationAuthority) getUnRevokedHandles(info *RevocationAuthorityInfo, revokedCreds []CredRecord) [][]byte { + log.Debugf("RA '%s' is getting revoked revocation handles for epoch %d", ra.issuer.Name, info.Epoch) + isRevokedHandle := func(rh []byte) bool { for i := 0; i <= len(revokedCreds)-1; i++ { rrhBytes, err := util.B64Decode(revokedCreds[i].RevocationHandle) if err != nil { log.Debugf("Failed to Base64 decode revocation handle '%s': %s", revokedCreds[i].RevocationHandle, err.Error()) return false } - if bytes.Equal(rh.Bytes(), rrhBytes) { + if bytes.Equal(rh, rrhBytes) { return true } } return false } - var validHandles []*math.Zr + var validHandles [][]byte for i := 1; i <= info.LastHandleInPool; i = i + 1 { - validHandles = append(validHandles, ra.curve.NewZrFromInt(int64(i))) + validHandles = append(validHandles, common.BigToBytes(big.NewInt(int64(i)))) } for i := len(validHandles) - 1; i >= 0; i-- { isrevoked := isRevokedHandle(validHandles[i]) @@ -293,7 +303,7 @@ func (ra *revocationAuthority) getNextRevocationHandleTx(tx db.FabricCATx, _ ... var inQuery string var args []interface{} if nextHandle == rcInfo.LastHandleInPool { - newLastHandleInPool := rcInfo.LastHandleInPool + ra.issuer.Config().RHPoolSize + newLastHandleInPool := rcInfo.LastHandleInPool + ra.issuer.Cfg.RHPoolSize newEpoch := rcInfo.Epoch + 1 query = UpdateNextAndLastHandle inQuery, args, err = sqlx.In(query, newNextHandle, newLastHandleInPool, newEpoch, rcInfo.Epoch) diff --git a/lib/server/idemix/revocationauthority_test.go b/lib/server/idemix/revocationauthority_test.go index a9bd7a46d..69ca0ed59 100644 --- a/lib/server/idemix/revocationauthority_test.go +++ b/lib/server/idemix/revocationauthority_test.go @@ -7,47 +7,38 @@ SPDX-License-Identifier: Apache-2.0 package idemix_test import ( - "bytes" - "crypto/ecdsa" - "fmt" + "math/big" "os" "path" "testing" - idmx "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" - math "github.com/IBM/mathlib" - cidemix "github.com/hyperledger/fabric-ca/lib/common/idemix" + "github.com/IBM/idemix/bccsp/types" + bccsp "github.com/IBM/idemix/bccsp/types" + "github.com/IBM/mathlib/driver/common" . "github.com/hyperledger/fabric-ca/lib/server/idemix" "github.com/hyperledger/fabric-ca/lib/server/idemix/mocks" dmocks "github.com/hyperledger/fabric-ca/lib/server/idemix/mocks" "github.com/hyperledger/fabric-ca/util" "github.com/pkg/errors" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) func TestLongTermKeyError(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testLongTermKeyError(t, curve) - }) - } -} - -func testLongTermKeyError(t *testing.T, curveID cidemix.CurveID) { - issuer := new(mocks.MyIssuer) - issuer.On("Name").Return("") - issuer.On("HomeDir").Return(".") + issuer := new(IssuerInst) + issuer.Name = "" + issuer.HomeDir = "." opts := &Config{ RHPoolSize: 100, RevocationPublicKeyfile: path.Join(".", DefaultRevocationPublicKeyFile), RevocationPrivateKeyfile: path.Join("./msp/keystore", DefaultRevocationPrivateKeyFile), } - issuer.On("Config").Return(opts) - lib := new(mocks.Lib) - lib.On("GenerateLongTermRevocationKey").Return(nil, errors.New("Failed to create revocation key")) - issuer.On("IdemixLib").Return(lib) - db := new(dmocks.FabricCADB) - issuer.On("DB").Return(db) - _, err := NewRevocationAuthority(issuer, 1, curveID) + issuer.Cfg = opts + db := new(dmocks.DbFabricCADB) + issuer.Db = db + mockCsp := new(mocks.BccspBCCSP) + issuer.Csp = mockCsp + mockCsp.On("KeyGen", &types.IdemixRevocationKeyGenOpts{Temporary: true}).Return(nil, errors.New("nope")) + _, err := NewRevocationAuthority(issuer, 1) assert.Error(t, err) if err != nil { assert.Contains(t, err.Error(), "Failed to generate revocation key for issuer") @@ -55,14 +46,6 @@ func testLongTermKeyError(t *testing.T, curveID cidemix.CurveID) { } func TestRevocationKeyLoadError(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testRevocationKeyLoadError(t, curve) - }) - } -} - -func testRevocationKeyLoadError(t *testing.T, curveID cidemix.CurveID) { homeDir := t.TempDir() err := os.MkdirAll(path.Join(homeDir, "msp/keystore"), 0o777) if err != nil { @@ -78,19 +61,17 @@ func testRevocationKeyLoadError(t *testing.T, curveID cidemix.CurveID) { if err != nil { t.Fatalf("Failed to write to file: %s", err.Error()) } - issuer := new(mocks.MyIssuer) - issuer.On("Name").Return("") - issuer.On("HomeDir").Return(homeDir) + issuer := new(IssuerInst) + issuer.Name = "" + issuer.HomeDir = homeDir opts := &Config{ RHPoolSize: 100, RevocationPublicKeyfile: revocationpubkeyfile, RevocationPrivateKeyfile: revocationprivkeyfile, } - issuer.On("Config").Return(opts) - lib := new(mocks.Lib) - issuer.On("IdemixLib").Return(lib) - db := new(dmocks.FabricCADB) - issuer.On("DB").Return(db) - _, err = NewRevocationAuthority(issuer, 1, curveID) + issuer.Cfg = opts + db := new(dmocks.DbFabricCADB) + issuer.Db = db + _, err = NewRevocationAuthority(issuer, 1) assert.Error(t, err) if err != nil { assert.Contains(t, err.Error(), "Failed to load revocation key for issuer") @@ -98,139 +79,89 @@ func testRevocationKeyLoadError(t *testing.T, curveID cidemix.CurveID) { } func TestGetRAInfoFromDBError(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testGetRAInfoFromDBError(t, curve) - }) - } -} - -func testGetRAInfoFromDBError(t *testing.T, curveID cidemix.CurveID) { homeDir := t.TempDir() err := os.MkdirAll(path.Join(homeDir, "msp/keystore"), 0o777) if err != nil { t.Fatalf("Failed to create directory: %s", err.Error()) } - issuer := new(mocks.MyIssuer) - issuer.On("Name").Return("ca1") - lib := new(mocks.Lib) - idemix := NewLib(curveID) - revocationKey, err := idemix.GenerateLongTermRevocationKey() - if err != nil { - t.Fatalf("Failed to generate test revocation key: %s", err.Error()) - } - lib.On("GenerateLongTermRevocationKey").Return(revocationKey, nil) - issuer.On("IdemixLib").Return(lib) + issuer := new(IssuerInst) + issuer.Name = "ca1" rainfos := []RevocationAuthorityInfo{} - db := new(dmocks.FabricCADB) + db := new(dmocks.DbFabricCADB) db.On("Select", "GetRAInfo", &rainfos, "SELECT * FROM revocation_authority_info"). Return(errors.New("Failed to execute select query")) - issuer.On("DB").Return(db) + issuer.Db = db + issuer.Csp = getCSP(t) opts := &Config{ RHPoolSize: 100, RevocationPublicKeyfile: path.Join(homeDir, DefaultRevocationPublicKeyFile), RevocationPrivateKeyfile: path.Join(homeDir, "msp/keystore", DefaultRevocationPrivateKeyFile), } - issuer.On("Config").Return(opts) - _, err = NewRevocationAuthority(issuer, 1, curveID) + issuer.Cfg = opts + _, err = NewRevocationAuthority(issuer, 1) assert.Error(t, err) } func TestGetRAInfoFromNewDBSelectError(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testGetRAInfoFromNewDBSelectError(t, curve) - }) - } -} - -func testGetRAInfoFromNewDBSelectError(t *testing.T, curveID cidemix.CurveID) { homeDir := t.TempDir() err := os.MkdirAll(path.Join(homeDir, "msp/keystore"), 0o777) if err != nil { t.Fatalf("Failed to create directory: %s", err.Error()) } - issuer := new(mocks.MyIssuer) - issuer.On("Name").Return("") - issuer.On("HomeDir").Return(homeDir) - lib := new(mocks.Lib) - idemix := NewLib(curveID) - revocationKey, err := idemix.GenerateLongTermRevocationKey() - if err != nil { - t.Fatalf("Failed to generate test revocation key: %s", err.Error()) - } - lib.On("GenerateLongTermRevocationKey").Return(revocationKey, nil) - issuer.On("IdemixLib").Return(lib) - db := new(dmocks.FabricCADB) + issuer := new(IssuerInst) + issuer.Name = "" + issuer.HomeDir = homeDir + db := new(dmocks.DbFabricCADB) raInfos := []RevocationAuthorityInfo{} f := getSelectFunc(t, true, true) db.On("Select", "GetRAInfo", &raInfos, SelectRAInfo).Return(f) - issuer.On("DB").Return(db) + issuer.Db = db + issuer.Csp = getCSP(t) opts := &Config{ RHPoolSize: 100, RevocationPublicKeyfile: path.Join(homeDir, DefaultRevocationPublicKeyFile), RevocationPrivateKeyfile: path.Join(homeDir, "msp/keystore", DefaultRevocationPrivateKeyFile), } - issuer.On("Config").Return(opts) - _, err = NewRevocationAuthority(issuer, 1, curveID) + issuer.Cfg = opts + _, err = NewRevocationAuthority(issuer, 1) assert.Error(t, err) } func TestGetRAInfoFromExistingDB(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testGetRAInfoFromExistingDB(t, curve) - }) - } -} - -func testGetRAInfoFromExistingDB(t *testing.T, curveID cidemix.CurveID) { homeDir := t.TempDir() err := os.MkdirAll(path.Join(homeDir, "msp/keystore"), 0o777) if err != nil { t.Fatalf("Failed to create directory: %s", err.Error()) } - issuer := new(mocks.MyIssuer) - issuer.On("Name").Return("") - issuer.On("HomeDir").Return(homeDir) - lib := new(mocks.Lib) - idemix := NewLib(curveID) - revocationKey, err := idemix.GenerateLongTermRevocationKey() - if err != nil { - t.Fatalf("Failed to generate test revocation key: %s", err.Error()) - } - issuer.On("IdemixLib").Return(lib) + issuer := new(IssuerInst) + issuer.Name = "" + issuer.HomeDir = homeDir + issuer.Csp = getCSP(t) rk := NewRevocationKey(path.Join(homeDir, DefaultRevocationPublicKeyFile), - path.Join(homeDir, "msp/keystore/", DefaultRevocationPrivateKeyFile), lib) + path.Join(homeDir, "msp/keystore/", DefaultRevocationPrivateKeyFile), getCSP(t)) + revocationKey, err := getCSP(t).KeyGen(&bccsp.IdemixRevocationKeyGenOpts{Temporary: true}) + assert.NoError(t, err) rk.SetKey(revocationKey) err = rk.Store() if err != nil { t.Fatalf("Failed to store test revocation key: %s", err.Error()) } - db := new(dmocks.FabricCADB) + db := new(dmocks.DbFabricCADB) raInfos := []RevocationAuthorityInfo{} f := getSelectFunc(t, false, false) db.On("Select", "GetRAInfo", &raInfos, SelectRAInfo).Return(f) - issuer.On("DB").Return(db) + issuer.Db = db opts := &Config{ RHPoolSize: 100, RevocationPublicKeyfile: path.Join(homeDir, DefaultRevocationPublicKeyFile), RevocationPrivateKeyfile: path.Join(homeDir, "msp/keystore", DefaultRevocationPrivateKeyFile), } - issuer.On("Config").Return(opts) - _, err = NewRevocationAuthority(issuer, 1, curveID) + issuer.Cfg = opts + _, err = NewRevocationAuthority(issuer, 1) assert.NoError(t, err) } func TestRevocationKeyStoreFailure(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testRevocationKeyStoreFailure(t, curve) - }) - } -} - -func testRevocationKeyStoreFailure(t *testing.T, curveID cidemix.CurveID) { homeDir := t.TempDir() - issuer, db, _ := setupForInsertTests(t, homeDir, curveID) + issuer, db, _ := setupForInsertTests(t, homeDir) os.RemoveAll(path.Join(homeDir, "msp/keystore")) rainfo := RevocationAuthorityInfo{ Epoch: 1, @@ -238,10 +169,11 @@ func testRevocationKeyStoreFailure(t *testing.T, curveID cidemix.CurveID) { LastHandleInPool: 100, Level: 1, } - result := new(dmocks.Result) + result := new(dmocks.SqlResult) result.On("RowsAffected").Return(int64(1), nil) db.On("NamedExec", InsertRAInfo, &rainfo).Return(result, nil) - issuer.On("DB").Return(db) + issuer.Db = db + issuer.Csp = getCSP(t) keystoreDir := path.Join(homeDir, "msp/keystore") err := os.MkdirAll(keystoreDir, 4444) if err != nil { @@ -251,8 +183,8 @@ func testRevocationKeyStoreFailure(t *testing.T, curveID cidemix.CurveID) { RHPoolSize: 100, RevocationPublicKeyfile: path.Join(homeDir, DefaultRevocationPublicKeyFile), RevocationPrivateKeyfile: path.Join(keystoreDir, DefaultRevocationPrivateKeyFile), } - issuer.On("Config").Return(opts) - _, err = NewRevocationAuthority(issuer, 1, curveID) + issuer.Cfg = opts + _, err = NewRevocationAuthority(issuer, 1) assert.Error(t, err) if err != nil { assert.Contains(t, err.Error(), "Failed to store revocation key of issuer") @@ -260,32 +192,25 @@ func testRevocationKeyStoreFailure(t *testing.T, curveID cidemix.CurveID) { } func TestGetRAInfoFromNewDBInsertFailure(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testGetRAInfoFromNewDBInsertFailure(t, curve) - }) - } -} - -func testGetRAInfoFromNewDBInsertFailure(t *testing.T, curveID cidemix.CurveID) { homeDir := t.TempDir() - issuer, db, _ := setupForInsertTests(t, homeDir, curveID) + issuer, db, _ := setupForInsertTests(t, homeDir) rainfo := RevocationAuthorityInfo{ Epoch: 1, NextRevocationHandle: 1, LastHandleInPool: 100, Level: 1, } - result := new(dmocks.Result) + result := new(dmocks.SqlResult) result.On("RowsAffected").Return(int64(0), nil) db.On("NamedExec", "AddRAInfo", InsertRAInfo, &rainfo).Return(result, nil) - issuer.On("DB").Return(db) + issuer.Db = db + issuer.Csp = getCSP(t) opts := &Config{ RHPoolSize: 100, RevocationPublicKeyfile: path.Join(homeDir, DefaultRevocationPublicKeyFile), RevocationPrivateKeyfile: path.Join(homeDir, "msp/keystore", DefaultRevocationPrivateKeyFile), } - issuer.On("Config").Return(opts) - _, err := NewRevocationAuthority(issuer, 1, curveID) + issuer.Cfg = opts + _, err := NewRevocationAuthority(issuer, 1) assert.Error(t, err) if err != nil { assert.Contains(t, err.Error(), "Failed to insert the revocation authority info record; no rows affected") @@ -293,37 +218,30 @@ func testGetRAInfoFromNewDBInsertFailure(t *testing.T, curveID cidemix.CurveID) } func TestGetRAInfoFromNewDBInsertFailure1(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testGetRAInfoFromNewDBInsertFailure1(t, curve) - }) - } -} - -func testGetRAInfoFromNewDBInsertFailure1(t *testing.T, curveID cidemix.CurveID) { homeDir := t.TempDir() err := os.MkdirAll(path.Join(homeDir, "msp/keystore"), 0o777) if err != nil { t.Fatalf("Failed to create directory: %s", err.Error()) } defer os.RemoveAll(homeDir) - issuer, db, _ := setupForInsertTests(t, homeDir, curveID) + issuer, db, _ := setupForInsertTests(t, homeDir) rainfo := RevocationAuthorityInfo{ Epoch: 1, NextRevocationHandle: 1, LastHandleInPool: 100, Level: 1, } - result := new(dmocks.Result) + result := new(dmocks.SqlResult) result.On("RowsAffected").Return(int64(2), nil) db.On("NamedExec", "AddRAInfo", InsertRAInfo, &rainfo).Return(result, nil) - issuer.On("DB").Return(db) + issuer.Db = db + issuer.Csp = getCSP(t) opts := &Config{ RHPoolSize: 100, RevocationPublicKeyfile: path.Join(homeDir, DefaultRevocationPublicKeyFile), RevocationPrivateKeyfile: path.Join(homeDir, "msp/keystore", DefaultRevocationPrivateKeyFile), } - issuer.On("Config").Return(opts) - _, err = NewRevocationAuthority(issuer, 1, curveID) + issuer.Cfg = opts + _, err = NewRevocationAuthority(issuer, 1) assert.Error(t, err) if err != nil { assert.Contains(t, err.Error(), "Expected to affect 1 entry in revocation authority info table but affected") @@ -331,16 +249,8 @@ func testGetRAInfoFromNewDBInsertFailure1(t *testing.T, curveID cidemix.CurveID) } func TestGetRAInfoFromNewDBInsertError(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testGetRAInfoFromNewDBInsertError(t, curve) - }) - } -} - -func testGetRAInfoFromNewDBInsertError(t *testing.T, curveID cidemix.CurveID) { homeDir := t.TempDir() - issuer, db, _ := setupForInsertTests(t, homeDir, curveID) + issuer, db, _ := setupForInsertTests(t, homeDir) rainfo := RevocationAuthorityInfo{ Epoch: 1, NextRevocationHandle: 1, @@ -349,13 +259,14 @@ func testGetRAInfoFromNewDBInsertError(t *testing.T, curveID cidemix.CurveID) { } db.On("NamedExec", "AddRAInfo", InsertRAInfo, &rainfo).Return(nil, errors.New("Inserting revocation authority info into DB failed")) - issuer.On("DB").Return(db) + issuer.Db = db + issuer.Csp = getCSP(t) opts := &Config{ RHPoolSize: 100, RevocationPublicKeyfile: path.Join(homeDir, DefaultRevocationPublicKeyFile), RevocationPrivateKeyfile: path.Join(homeDir, "msp/keystore", DefaultRevocationPrivateKeyFile), } - issuer.On("Config").Return(opts) - _, err := NewRevocationAuthority(issuer, 1, curveID) + issuer.Cfg = opts + _, err := NewRevocationAuthority(issuer, 1) assert.Error(t, err) if err != nil { assert.Contains(t, err.Error(), "Failed to insert revocation authority info into database") @@ -363,20 +274,12 @@ func testGetRAInfoFromNewDBInsertError(t *testing.T, curveID cidemix.CurveID) { } func TestGetNewRevocationHandleSelectError(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testGetNewRevocationHandleSelectError(t, curve) - }) - } -} - -func testGetNewRevocationHandleSelectError(t *testing.T, curveID cidemix.CurveID) { homeDir := t.TempDir() - db := new(dmocks.FabricCADB) + db := new(dmocks.DbFabricCADB) selectFnc := getSelectFunc(t, true, false) - ra := getRevocationAuthority(t, "GetNextRevocationHandle", homeDir, db, nil, 0, false, false, curveID, selectFnc) + ra := getRevocationAuthority(t, "GetNextRevocationHandle", homeDir, db, nil, 0, false, false, selectFnc) - tx := new(dmocks.FabricCATx) + tx := new(dmocks.DbFabricCATx) tx.On("Commit", "GetNextRevocationHandle").Return(nil) tx.On("Rollback", "GetNextRevocationHandle").Return(nil) tx.On("Rebind", SelectRAInfo).Return(SelectRAInfo) @@ -396,20 +299,12 @@ func testGetNewRevocationHandleSelectError(t *testing.T, curveID cidemix.CurveID } func TestGetNewRevocationHandleNoData(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testGetNewRevocationHandleNoData(t, curve) - }) - } -} - -func testGetNewRevocationHandleNoData(t *testing.T, curveID cidemix.CurveID) { homeDir := t.TempDir() - db := new(dmocks.FabricCADB) + db := new(dmocks.DbFabricCADB) selectFnc := getSelectFunc(t, true, false) - ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, nil, 0, false, false, curveID, selectFnc) + ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, nil, 0, false, false, selectFnc) - tx := new(dmocks.FabricCATx) + tx := new(dmocks.DbFabricCATx) tx.On("Commit", "GetNextRevocationHandle").Return(nil) tx.On("Rollback", "GetNextRevocationHandle").Return(nil) tx.On("Rebind", SelectRAInfo).Return(SelectRAInfo) @@ -428,20 +323,12 @@ func testGetNewRevocationHandleNoData(t *testing.T, curveID cidemix.CurveID) { } func TestGetNewRevocationHandleExecError(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testGetNewRevocationHandleExecError(t, curve) - }) - } -} - -func testGetNewRevocationHandleExecError(t *testing.T, curveID cidemix.CurveID) { homeDir := t.TempDir() - db := new(dmocks.FabricCADB) + db := new(dmocks.DbFabricCADB) selectFnc := getSelectFunc(t, true, false) - ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, nil, 0, false, false, curveID, selectFnc) + ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, nil, 0, false, false, selectFnc) - tx := new(dmocks.FabricCATx) + tx := new(dmocks.DbFabricCATx) rcInfos := []RevocationAuthorityInfo{} fnc := getTxSelectFunc(t, &rcInfos, 1, false, true) tx.On("Select", "GetRAInfo", &rcInfos, SelectRAInfo).Return(fnc) @@ -460,20 +347,12 @@ func testGetNewRevocationHandleExecError(t *testing.T, curveID cidemix.CurveID) } func TestGetNewRevocationHandleRollbackError(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testGetNewRevocationHandleRollbackError(t, curve) - }) - } -} - -func testGetNewRevocationHandleRollbackError(t *testing.T, curveID cidemix.CurveID) { homeDir := t.TempDir() - db := new(dmocks.FabricCADB) + db := new(dmocks.DbFabricCADB) selectFnc := getSelectFunc(t, true, false) - ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, nil, 0, false, false, curveID, selectFnc) + ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, nil, 0, false, false, selectFnc) - tx := new(dmocks.FabricCATx) + tx := new(dmocks.DbFabricCATx) rcInfos := []RevocationAuthorityInfo{} fnc := getTxSelectFunc(t, &rcInfos, 1, false, true) tx.On("Select", "GetRAInfo", &rcInfos, SelectRAInfo).Return(fnc) @@ -492,20 +371,12 @@ func testGetNewRevocationHandleRollbackError(t *testing.T, curveID cidemix.Curve } func TestGetNewRevocationHandleCommitError(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testGetNewRevocationHandleCommitError(t, curve) - }) - } -} - -func testGetNewRevocationHandleCommitError(t *testing.T, curveID cidemix.CurveID) { homeDir := t.TempDir() - db := new(dmocks.FabricCADB) + db := new(dmocks.DbFabricCADB) selectFnc := getSelectFunc(t, true, false) - ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, nil, 0, false, false, curveID, selectFnc) + ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, nil, 0, false, false, selectFnc) - tx := new(dmocks.FabricCATx) + tx := new(dmocks.DbFabricCATx) tx.On("Commit", "GetNextRevocationHandle").Return(errors.New("Error commiting")) tx.On("Rollback").Return(nil) tx.On("Rebind", SelectRAInfo).Return(SelectRAInfo) @@ -522,20 +393,12 @@ func testGetNewRevocationHandleCommitError(t *testing.T, curveID cidemix.CurveID } func TestGetNewRevocationHandle(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testGetNewRevocationHandle(t, curve) - }) - } -} - -func testGetNewRevocationHandle(t *testing.T, curveID cidemix.CurveID) { homeDir := t.TempDir() - db := new(dmocks.FabricCADB) + db := new(dmocks.DbFabricCADB) selectFnc := getSelectFunc(t, true, false) - rc := getRevocationAuthority(t, "GetRAInfo", homeDir, db, nil, 0, false, false, curveID, selectFnc) + rc := getRevocationAuthority(t, "GetRAInfo", homeDir, db, nil, 0, false, false, selectFnc) - tx := new(dmocks.FabricCATx) + tx := new(dmocks.DbFabricCATx) tx.On("Commit", "GetNextRevocationHandle").Return(nil) tx.On("Rollback", "GetNextRevocationHandle").Return(nil) tx.On("Rebind", SelectRAInfo).Return(SelectRAInfo) @@ -549,26 +412,16 @@ func testGetNewRevocationHandle(t *testing.T, curveID cidemix.CurveID) { rh, err := rc.GetNewRevocationHandle() assert.NoError(t, err) - curve := cidemix.CurveByID(curveID) - - assert.Equal(t, 0, bytes.Compare(curve.NewZrFromInt(1).Bytes(), rh.Bytes()), "Expected next revocation handle to be 1") + assert.Equal(t, int64(1), rh, "Expected next revocation handle to be 1") } func TestGetNewRevocationHandleLastHandle(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testGetNewRevocationHandleLastHandle(t, curve) - }) - } -} - -func testGetNewRevocationHandleLastHandle(t *testing.T, curveID cidemix.CurveID) { homeDir := t.TempDir() - db := new(dmocks.FabricCADB) + db := new(dmocks.DbFabricCADB) selectFnc := getSelectFunc(t, true, false) - ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, nil, 0, false, false, curveID, selectFnc) + ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, nil, 0, false, false, selectFnc) - tx := new(dmocks.FabricCATx) + tx := new(dmocks.DbFabricCATx) tx.On("Commit", "GetNextRevocationHandle").Return(nil) tx.On("Rollback", "GetNextRevocationHandle").Return(nil) tx.On("Rebind", SelectRAInfo).Return(SelectRAInfo) @@ -582,26 +435,14 @@ func testGetNewRevocationHandleLastHandle(t *testing.T, curveID cidemix.CurveID) rh, err := ra.GetNewRevocationHandle() assert.NoError(t, err) - curve := cidemix.CurveByID(curveID) - assert.Equal(t, 0, bytes.Compare(curve.NewZrFromInt(100).Bytes(), rh.Bytes()), - "Expected next revocation handle to be 100") - assert.Equal(t, util.B64Encode(curve.NewZrFromInt(100).Bytes()), util.B64Encode(rh.Bytes()), - "Expected next revocation handle to be 100") + assert.Equal(t, int64(100), rh, "Expected next revocation handle to be 100") } func TestGetEpoch(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testGetEpoch(t, curve) - }) - } -} - -func testGetEpoch(t *testing.T, curveID cidemix.CurveID) { homeDir := t.TempDir() - db := new(dmocks.FabricCADB) + db := new(dmocks.DbFabricCADB) selectFnc := getSelectFunc(t, true, false) - ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, nil, 0, false, false, curveID, selectFnc) + ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, nil, 0, false, false, selectFnc) rcInfos := []RevocationAuthorityInfo{} db.On("Select", "GetRAInfo", &rcInfos, SelectRAInfo).Return(selectFnc) @@ -613,156 +454,96 @@ func testGetEpoch(t *testing.T, curveID cidemix.CurveID) { } func TestGetEpochRAInfoError(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testGetEpochRAInfoError(t, curve) - }) - } -} - -func testGetEpochRAInfoError(t *testing.T, curveID cidemix.CurveID) { homeDir := t.TempDir() - db := new(dmocks.FabricCADB) - idemix := NewLib(curveID) + db := new(dmocks.DbFabricCADB) + + revocationKey, err := getCSP(t).KeyGen(&bccsp.IdemixRevocationKeyGenOpts{Temporary: true}) + assert.NoError(t, err) - revocationKey, err := idemix.GenerateLongTermRevocationKey() - if err != nil { - t.Fatalf("Failed to generate ECDSA key for revocation authority") - } selectFnc := getSelectFuncForCreateCRI(t, true, true) - ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, revocationKey, 0, false, false, curveID, selectFnc) + ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, revocationKey, 0, false, false, selectFnc) _, err = ra.Epoch() assert.Error(t, err, "Epoch should fail if there is an error getting revocation info from DB") } func TestCreateCRIGetRAInfoError(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testCreateCRIGetRAInfoError(t, curve) - }) - } -} - -func testCreateCRIGetRAInfoError(t *testing.T, curveID cidemix.CurveID) { homeDir := t.TempDir() - db := new(dmocks.FabricCADB) - idemix := NewLib(curveID) + db := new(dmocks.DbFabricCADB) + + revocationKey, err := getCSP(t).KeyGen(&bccsp.IdemixRevocationKeyGenOpts{Temporary: true}) + assert.NoError(t, err) - revocationKey, err := idemix.GenerateLongTermRevocationKey() - if err != nil { - t.Fatalf("Failed to generate ECDSA key for revocation authority") - } selectFnc := getSelectFuncForCreateCRI(t, true, true) - ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, revocationKey, 0, false, false, curveID, selectFnc) + ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, revocationKey, 0, false, false, selectFnc) _, err = ra.CreateCRI() assert.Error(t, err, "CreateCRI should fail if there is an error getting revocation info from DB") } func TestCreateCRIGetRevokeCredsError(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testCreateCRIGetRevokeCredsError(t, curve) - }) - } -} - -func testCreateCRIGetRevokeCredsError(t *testing.T, curveID cidemix.CurveID) { homeDir := t.TempDir() - db := new(dmocks.FabricCADB) - idemix := NewLib(curveID) + db := new(dmocks.DbFabricCADB) - revocationKey, err := idemix.GenerateLongTermRevocationKey() - if err != nil { - t.Fatalf("Failed to generate ECDSA key for revocation authority") - } + revocationKey, err := getCSP(t).KeyGen(&bccsp.IdemixRevocationKeyGenOpts{Temporary: true}) + assert.NoError(t, err) selectFnc := getSelectFuncForCreateCRI(t, true, false) - ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, revocationKey, 0, true, false, curveID, selectFnc) + ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, revocationKey, 0, true, false, selectFnc) _, err = ra.CreateCRI() assert.Error(t, err, "CreateCRI should fail if there is an error getting revoked credentials") } func TestIdemixCreateCRIError(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testIdemixCreateCRIError(t, curve) - }) - } -} - -func testIdemixCreateCRIError(t *testing.T, curveID cidemix.CurveID) { homeDir := t.TempDir() - idemix := NewLib(curveID) - revocationKey, err := idemix.GenerateLongTermRevocationKey() - if err != nil { - t.Fatalf("Failed to generate ECDSA key for revocation authority") - } + revocationKey, err := getCSP(t).KeyGen(&bccsp.IdemixRevocationKeyGenOpts{Temporary: true}) + assert.NoError(t, err) - db := new(dmocks.FabricCADB) + db := new(dmocks.DbFabricCADB) selectFnc := getSelectFuncForCreateCRI(t, true, false) - ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, revocationKey, 0, false, true, curveID, selectFnc) + ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, revocationKey, 0, false, true, selectFnc) _, err = ra.CreateCRI() assert.Error(t, err, "CreateCRI should fail if idemix.CreateCRI returns an error") } func TestEpochValuesInCRI(t *testing.T) { - for _, curve := range cidemix.Curves { - t.Run(fmt.Sprintf("%s-%d", t.Name(), curve), func(t *testing.T) { - testEpochValuesInCRI(t, curve) - }) - } -} - -func testEpochValuesInCRI(t *testing.T, curveID cidemix.CurveID) { homeDir := t.TempDir() - idemix := NewLib(curveID) - revocationKey, err := idemix.GenerateLongTermRevocationKey() - if err != nil { - t.Fatalf("Failed to generate ECDSA key for revocation authority") - } + revocationKey, err := getCSP(t).KeyGen(&bccsp.IdemixRevocationKeyGenOpts{Temporary: true}) + assert.NoError(t, err) selectFnc := getSelectFuncForCreateCRI(t, true, false) - db := new(dmocks.FabricCADB) - ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, revocationKey, 0, false, false, curveID, selectFnc) - cri, err := ra.CreateCRI() + db := new(dmocks.DbFabricCADB) + ra := getRevocationAuthority(t, "GetRAInfo", homeDir, db, revocationKey, 0, false, false, selectFnc) + _, err = ra.CreateCRI() assert.NoError(t, err) - cri1, err := ra.CreateCRI() + _, err = ra.CreateCRI() assert.NoError(t, err) - if err == nil { - assert.Equal(t, cri.Epoch, cri1.Epoch) - } } -func setupForInsertTests(t *testing.T, homeDir string, curveID cidemix.CurveID) (*mocks.MyIssuer, *dmocks.FabricCADB, *ecdsa.PrivateKey) { - issuer := new(mocks.MyIssuer) - issuer.On("Name").Return("") - issuer.On("HomeDir").Return(homeDir) +func setupForInsertTests(t *testing.T, homeDir string) (*IssuerInst, *dmocks.DbFabricCADB, types.Key) { + issuer := new(IssuerInst) + issuer.Name = "" + issuer.HomeDir = homeDir keystore := path.Join(homeDir, "msp/keystore") err := os.MkdirAll(keystore, 0o777) if err != nil { t.Fatalf("Failed to create directory %s: %s", keystore, err.Error()) } - lib := new(mocks.Lib) - idemix := NewLib(curveID) - privateKey, err := idemix.GenerateLongTermRevocationKey() - if err != nil { - t.Fatalf("Failed to generate ECDSA key for revocation authority") - } - lib.On("GenerateLongTermRevocationKey").Return(privateKey, nil) - issuer.On("IdemixLib").Return(lib) - db := new(dmocks.FabricCADB) + + RevocationKey, err := getCSP(t).KeyGen(&bccsp.IdemixRevocationKeyGenOpts{Temporary: true}) + assert.NoError(t, err) + + db := new(dmocks.DbFabricCADB) rcInfos := []RevocationAuthorityInfo{} f := getSelectFunc(t, true, false) db.On("Select", "GetRAInfo", &rcInfos, SelectRAInfo).Return(f) - return issuer, db, privateKey + return issuer, db, RevocationKey } -func getRevocationAuthority(t *testing.T, funcName, homeDir string, db *dmocks.FabricCADB, revocationKey *ecdsa.PrivateKey, revokedCred int, - getRevokedCredsError bool, idemixCreateCRIError bool, curveID cidemix.CurveID, selectFnc func(string, interface{}, string, ...interface{}) error) RevocationAuthority { - issuer := new(mocks.MyIssuer) - issuer.On("Name").Return("ca1") - issuer.On("HomeDir").Return(homeDir) +func getRevocationAuthority(t *testing.T, funcName, homeDir string, db *dmocks.DbFabricCADB, revocationKey types.Key, revokedCred int, + getRevokedCredsError bool, idemixCreateCRIError bool, selectFnc func(string, interface{}, string, ...interface{}) error) RevocationAuthority { + issuer := new(IssuerInst) + issuer.Name = "ca1" + issuer.HomeDir = homeDir keystore := path.Join(homeDir, "msp/keystore") err := os.MkdirAll(keystore, 0o777) if err != nil { @@ -770,16 +551,10 @@ func getRevocationAuthority(t *testing.T, funcName, homeDir string, db *dmocks.F } if revocationKey == nil { - var err error - idemix := NewLib(curveID) - revocationKey, err = idemix.GenerateLongTermRevocationKey() - if err != nil { - t.Fatalf("Failed to generate ECDSA key for revocation authority") - } + RevocationKey, err := getCSP(t).KeyGen(&bccsp.IdemixRevocationKeyGenOpts{Temporary: true}) + assert.NoError(t, err) + revocationKey = RevocationKey } - lib := new(mocks.Lib) - lib.On("GenerateLongTermRevocationKey").Return(revocationKey, nil) - issuer.On("IdemixLib").Return(lib) rcInfosForSelect := []RevocationAuthorityInfo{} db.On("Select", "GetRAInfo", &rcInfosForSelect, SelectRAInfo).Return(selectFnc) @@ -789,23 +564,16 @@ func getRevocationAuthority(t *testing.T, funcName, homeDir string, db *dmocks.F LastHandleInPool: 100, Level: 1, } - result := new(dmocks.Result) + result := new(dmocks.SqlResult) result.On("RowsAffected").Return(int64(1), nil) db.On("NamedExec", "AddRAInfo", InsertRAInfo, &rcinfo).Return(result, nil) - issuer.On("DB").Return(db) + issuer.Db = db + issuer.Csp = getCSP(t) cfg := &Config{ RHPoolSize: 100, RevocationPublicKeyfile: path.Join(homeDir, DefaultRevocationPublicKeyFile), RevocationPrivateKeyfile: path.Join(homeDir, "msp/keystore", DefaultRevocationPrivateKeyFile), } - issuer.On("Config").Return(cfg) - - curve := cidemix.CurveByID(curveID) - - rand, err := curve.Rand() - if err != nil { - t.Fatalf("Failed generate random number: %s", err.Error()) - } - issuer.On("IdemixRand").Return(rand) + issuer.Cfg = cfg credDBAccessor := new(mocks.CredDBAccessor) if getRevokedCredsError { @@ -813,7 +581,7 @@ func getRevocationAuthority(t *testing.T, funcName, homeDir string, db *dmocks.F } else { revokedCreds := []CredRecord{} if revokedCred > 0 { - rh := util.B64Encode(curve.NewZrFromInt(int64(revokedCred)).Bytes()) + rh := util.B64Encode(common.BigToBytes(big.NewInt(int64(revokedCred)))) cr := CredRecord{ RevocationHandle: rh, Cred: "", @@ -825,29 +593,19 @@ func getRevocationAuthority(t *testing.T, funcName, homeDir string, db *dmocks.F credDBAccessor.On("GetRevokedCredentials").Return(revokedCreds, nil) } - issuer.On("CredDBAccessor").Return(credDBAccessor) - - idemix := cidemix.InstanceForCurve(curveID) + issuer.CredDBAccessor = credDBAccessor - var validHandles []*math.Zr - for i := 1; i <= 100; i = i + 1 { - validHandles = append(validHandles, curve.NewZrFromInt(int64(i))) + ra, err := NewRevocationAuthority(issuer, 1) + if err != nil { + t.Fatalf("Failed to get revocation authority instance: %s", err.Error()) } - alg := idmx.ALG_NO_REVOCATION + if idemixCreateCRIError { - lib.On("CreateCRI", revocationKey, validHandles, 1, alg).Return(nil, errors.New("Idemix lib create CRI error")) - } else { - cri, err := idemix.CreateCRI(revocationKey, validHandles, 1, alg, rand, idemix.Translator) - if err != nil { - t.Fatalf("Failed to create CRI: %s", err.Error()) - } - lib.On("CreateCRI", revocationKey, validHandles, 1, alg).Return(cri, nil) + mockCsp := new(mocks.BccspBCCSP) + mockCsp.On("Sign", mock.Anything, mock.Anything, mock.Anything).Return(nil, errors.New("super bad")) + issuer.Csp = mockCsp } - ra, err := NewRevocationAuthority(issuer, 1, curveID) - if err != nil { - t.Fatalf("Failed to get revocation authority instance: %s", err.Error()) - } return ra } diff --git a/lib/server/idemix/revocationkey.go b/lib/server/idemix/revocationkey.go index bff1fe553..a1a7818a1 100644 --- a/lib/server/idemix/revocationkey.go +++ b/lib/server/idemix/revocationkey.go @@ -7,11 +7,11 @@ SPDX-License-Identifier: Apache-2.0 package idemix import ( - "crypto/ecdsa" - "crypto/x509" "encoding/pem" "os" + "github.com/IBM/idemix/bccsp/types" + bccsp "github.com/IBM/idemix/bccsp/types" "github.com/cloudflare/cfssl/log" "github.com/hyperledger/fabric-ca/util" "github.com/pkg/errors" @@ -23,10 +23,10 @@ type RevocationKey interface { Load() error // Store stores this revocation key to the disk Store() error - // GetKey returns *ecdsa.PrivateKey that represents revocation public and private key pair - GetKey() *ecdsa.PrivateKey + // GetKey returns bccsp.Key that represents revocation public and private key pair + GetKey() bccsp.Key // SetKey sets revocation public and private key - SetKey(key *ecdsa.PrivateKey) + SetKey(key bccsp.Key) // SetNewKey creates new revocation public and private key pair and sets them in this object SetNewKey() error } @@ -35,16 +35,16 @@ type RevocationKey interface { type caIdemixRevocationKey struct { pubKeyFile string privateKeyFile string - key *ecdsa.PrivateKey - idemixLib Lib + key bccsp.Key + CSP bccsp.BCCSP } // NewRevocationKey returns an instance of an object that implements RevocationKey interface -func NewRevocationKey(pubKeyFile, privateKeyFile string, lib Lib) RevocationKey { +func NewRevocationKey(pubKeyFile, privateKeyFile string, CSP bccsp.BCCSP) RevocationKey { return &caIdemixRevocationKey{ pubKeyFile: pubKeyFile, privateKeyFile: privateKeyFile, - idemixLib: lib, + CSP: CSP, } } @@ -58,19 +58,25 @@ func (rk *caIdemixRevocationKey) Load() error { if len(pubKeyBytes) == 0 { return errors.New("Revocation public key file is empty") } - privKey, err := os.ReadFile(rk.privateKeyFile) + privKeyBytes, err := os.ReadFile(rk.privateKeyFile) if err != nil { return errors.Wrapf(err, "Failed to read revocation private key from %s", rk.privateKeyFile) } - if len(privKey) == 0 { + if len(privKeyBytes) == 0 { return errors.New("Revocation private key file is empty") } - pk, pubKey, err := DecodeKeys(privKey, pubKeyBytes) + + revPrivKey, err := rk.CSP.KeyImport(privKeyBytes, &bccsp.IdemixRevocationKeyImportOpts{Temporary: true}) + if err != nil { + return errors.Wrapf(err, "Failed to import revocation private key") + } + + _, err = rk.CSP.KeyImport(pubKeyBytes, &bccsp.IdemixRevocationPublicKeyImportOpts{Temporary: true}) if err != nil { - return errors.WithMessage(err, "Failed to decode revocation key") + return errors.Wrapf(err, "Failed to import revocation public key") } - pk.PublicKey = *pubKey - rk.key = pk + + rk.key = revPrivKey return nil } @@ -81,11 +87,25 @@ func (rk *caIdemixRevocationKey) Store() error { if pk == nil { return errors.New("Revocation key is not set") } - pkBytes, pubKeyBytes, err := EncodeKeys(pk, &pk.PublicKey) + + privKeyBytes, err := pk.Bytes() + if err != nil { + return errors.Wrapf(err, "Failed to serialise revocation private key") + } + + pubKey, err := pk.PublicKey() if err != nil { - return errors.WithMessage(err, "Failed to encode revocation public key") + return errors.Wrapf(err, "Failed to convert revocation private key to public") } - err = util.WriteFile(rk.privateKeyFile, []byte(pkBytes), 0644) + + pubKeyBytes, err := pubKey.Bytes() + if err != nil { + return errors.Wrapf(err, "Failed to serialise revocation public key") + } + + pubKeyBytes = pem.EncodeToMemory(&pem.Block{Type: "ECDSA Public Key", Bytes: pubKeyBytes}) + + err = util.WriteFile(rk.privateKeyFile, []byte(privKeyBytes), 0644) if err != nil { log.Errorf("Failed to store revocation private key: %s", err.Error()) return errors.Wrapf(err, "Failed to store revocation private key at %s", rk.privateKeyFile) @@ -103,56 +123,54 @@ func (rk *caIdemixRevocationKey) Store() error { } // GetKey returns revocation key -func (rk *caIdemixRevocationKey) GetKey() *ecdsa.PrivateKey { +func (rk *caIdemixRevocationKey) GetKey() bccsp.Key { return rk.key } // SetKey sets revocation key -func (rk *caIdemixRevocationKey) SetKey(key *ecdsa.PrivateKey) { +func (rk *caIdemixRevocationKey) SetKey(key bccsp.Key) { rk.key = key } // SetNewKey creates new revocation key and sets it in this object func (rk *caIdemixRevocationKey) SetNewKey() (err error) { - rk.key, err = rk.idemixLib.GenerateLongTermRevocationKey() - return err + RevocationKey, err := rk.CSP.KeyGen(&bccsp.IdemixRevocationKeyGenOpts{Temporary: true}) + if err != nil { + return err + } + + rk.key = RevocationKey + return nil } // EncodeKeys encodes ECDSA key pair to PEM encoding -func EncodeKeys(privateKey *ecdsa.PrivateKey, publicKey *ecdsa.PublicKey) ([]byte, []byte, error) { - encodedPK, err := x509.MarshalECPrivateKey(privateKey) +func EncodeKeys(privateKey, publicKey types.Key) ([]byte, []byte, error) { + privateKeyBytes, err := privateKey.Bytes() if err != nil { - return nil, nil, errors.Wrap(err, "Failed to encode ECDSA private key") + return nil, nil, errors.Wrap(err, "Failed to encode private key") } - pemEncodedPK := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: encodedPK}) - encodedPubKey, err := x509.MarshalPKIXPublicKey(publicKey) + publicKeyBytes, err := publicKey.Bytes() if err != nil { - return nil, nil, errors.Wrap(err, "Failed to encode ECDSA public key") + return nil, nil, errors.Wrap(err, "Failed to encode public key") } - pemEncodedPubKey := pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: encodedPubKey}) - return pemEncodedPK, pemEncodedPubKey, nil + + publicKeyBytes = pem.EncodeToMemory(&pem.Block{Type: "ECDSA Public Key", Bytes: publicKeyBytes}) + + return privateKeyBytes, publicKeyBytes, nil } // DecodeKeys decodes ECDSA key pair that are pem encoded -func DecodeKeys(pemEncodedPK, pemEncodedPubKey []byte) (*ecdsa.PrivateKey, *ecdsa.PublicKey, error) { - block, _ := pem.Decode(pemEncodedPK) - if block == nil { - return nil, nil, errors.New("Failed to decode ECDSA private key") - } - pk, err := x509.ParseECPrivateKey(block.Bytes) +func DecodeKeys(privateKeyBytes, publicKeyBytes []byte, csp types.BCCSP) (types.Key, types.Key, error) { + privateKey, err := csp.KeyImport(privateKeyBytes, &types.IdemixRevocationKeyImportOpts{Temporary: true}) if err != nil { - return nil, nil, errors.Wrap(err, "Failed to parse ECDSA private key bytes") + return nil, nil, errors.Wrap(err, "Failed to parse private key bytes") } - blockPub, _ := pem.Decode(pemEncodedPubKey) - if blockPub == nil { - return nil, nil, errors.New("Failed to decode ECDSA public key") - } - key, err := x509.ParsePKIXPublicKey(blockPub.Bytes) + + publicKey, err := csp.KeyImport(publicKeyBytes, &types.IdemixRevocationPublicKeyImportOpts{Temporary: true}) if err != nil { - return nil, nil, errors.Wrap(err, "Failed to parse ECDSA public key bytes") + return nil, nil, errors.Wrap(err, "Failed to parse public key bytes") } - publicKey := key.(*ecdsa.PublicKey) - return pk, publicKey, nil + return privateKey, publicKey, nil } diff --git a/lib/server/idemix/revocationkey_test.go b/lib/server/idemix/revocationkey_test.go index 37173928c..ca93af331 100644 --- a/lib/server/idemix/revocationkey_test.go +++ b/lib/server/idemix/revocationkey_test.go @@ -7,13 +7,13 @@ SPDX-License-Identifier: Apache-2.0 package idemix_test import ( - "crypto/ecdsa" + "errors" "os" "path" "path/filepath" "testing" - cidemix "github.com/hyperledger/fabric-ca/lib/common/idemix" + bccsp "github.com/IBM/idemix/bccsp/types" . "github.com/hyperledger/fabric-ca/lib/server/idemix" "github.com/hyperledger/fabric-ca/lib/server/idemix/mocks" "github.com/hyperledger/fabric-ca/util" @@ -27,7 +27,7 @@ const ( func TestLoadNonExistentRevocationPublicKey(t *testing.T) { testdir := t.TempDir() - idemixLib := new(mocks.Lib) + idemixLib := getCSP(t) rk := NewRevocationKey(path.Join(testdir, DefaultRevocationPublicKeyFile), path.Join(testdir, "msp/keystore", DefaultRevocationPrivateKeyFile), idemixLib) err := rk.Load() @@ -40,7 +40,7 @@ func TestLoadNonExistentRevocationPublicKey(t *testing.T) { func TestLoadEmptyRevocationPublicKey(t *testing.T) { testdir := t.TempDir() pubkeyfile, err := os.CreateTemp(testdir, DefaultRevocationPublicKeyFile) - idemixLib := new(mocks.Lib) + idemixLib := getCSP(t) rk := NewRevocationKey(pubkeyfile.Name(), path.Join(testdir, "msp/keystore", DefaultRevocationPrivateKeyFile), idemixLib) err = rk.Load() assert.Error(t, err, "Should have failed to load empty revocation public key") @@ -51,8 +51,6 @@ func TestLoadEmptyRevocationPublicKey(t *testing.T) { func TestLoadFakeRevocationPublicKey(t *testing.T) { testdir := t.TempDir() - idemixLib := new(mocks.Lib) - idemix := cidemix.InstanceForCurve(cidemix.FP256BN) pubkeyfile, err := os.CreateTemp(testdir, DefaultRevocationPublicKeyFile) if err != nil { t.Fatalf("Failed to create temp file: %s", err.Error()) @@ -61,11 +59,15 @@ func TestLoadFakeRevocationPublicKey(t *testing.T) { if err != nil { t.Fatalf("Failed to create temp file: %s", err.Error()) } - key, err := idemix.GenerateLongTermRevocationKey() + RevocationPrivateKey, err := getCSP(t).KeyGen(&bccsp.IdemixRevocationKeyGenOpts{Temporary: true}) if err != nil { t.Fatalf("Failed to generate test revocation key: %s", err.Error()) } - privKey, _, err := EncodeKeys(key, &key.PublicKey) + RevocationPublicKey, err := RevocationPrivateKey.PublicKey() + if err != nil { + t.Fatalf("Failed to obtain public key") + } + privKey, _, err := EncodeKeys(RevocationPublicKey, RevocationPrivateKey) if err != nil { t.Fatalf("Failed to encode test revocation key: %s", err.Error()) } @@ -78,17 +80,18 @@ func TestLoadFakeRevocationPublicKey(t *testing.T) { if err != nil { t.Fatalf("Failed to write to the file %s", pubkeyfile.Name()) } + idemixLib := getCSP(t) rk := NewRevocationKey(pubkeyfile.Name(), privkeyfile.Name(), idemixLib) err = rk.Load() assert.Error(t, err, "Should have failed to load non existing revocation public key") if err != nil { - assert.Contains(t, err.Error(), "Failed to decode ECDSA public key") + assert.Contains(t, err.Error(), "Failed to decode revocation ECDSA public key") } } func TestLoadNonExistentRevocationPrivateKey(t *testing.T) { testdir := t.TempDir() - idemixLib := new(mocks.Lib) + idemixLib := getCSP(t) rk := NewRevocationKey(testRevocationPublicKeyFile, filepath.Join(testdir, "IdemixRevocationPrivateKey"), idemixLib) err := rk.Load() assert.Error(t, err, "Should have failed to load non existing issuer revocation private key") @@ -100,7 +103,7 @@ func TestLoadNonExistentRevocationPrivateKey(t *testing.T) { func TestLoadEmptyRevocationPrivateKey(t *testing.T) { testdir := t.TempDir() privkeyfile, err := os.CreateTemp(testdir, "") - idemixLib := new(mocks.Lib) + idemixLib := getCSP(t) rk := NewRevocationKey(testRevocationPublicKeyFile, privkeyfile.Name(), idemixLib) err = rk.Load() assert.Error(t, err, "Should have failed to load empty issuer revocation private key") @@ -110,7 +113,7 @@ func TestLoadEmptyRevocationPrivateKey(t *testing.T) { } func TestRevocationKeyLoad(t *testing.T) { - idemixLib := new(mocks.Lib) + idemixLib := getCSP(t) rk := NewRevocationKey(testRevocationPublicKeyFile, testRevocationPrivateKeyFile, idemixLib) err := rk.Load() assert.NoError(t, err, "Failed to load Idemix issuer revocation key") @@ -120,7 +123,7 @@ func TestRevocationKeyLoad(t *testing.T) { } func TestStoreNilRevocationKey(t *testing.T) { - idemixLib := new(mocks.Lib) + idemixLib := getCSP(t) rk := NewRevocationKey(testRevocationPublicKeyFile, testRevocationPrivateKeyFile, idemixLib) err := rk.Store() assert.Error(t, err, "Should fail if store is called without setting or loading the revocation key from disk") @@ -130,16 +133,12 @@ func TestStoreNilRevocationKey(t *testing.T) { } func TestStoreNilRevocationPublicKey(t *testing.T) { - idemixLib := new(mocks.Lib) - idemix := cidemix.InstanceForCurve(cidemix.FP256BN) + idemixLib := getCSP(t) rk := NewRevocationKey(testRevocationPublicKeyFile, testRevocationPrivateKeyFile, idemixLib) - key, err := idemix.GenerateLongTermRevocationKey() - if err != nil { - t.Fatalf("Failed to create test revocation key: %s", err.Error()) - } - key.PublicKey = ecdsa.PublicKey{} - rk.SetKey(key) - err = rk.Store() + mk := new(mocks.BccspKey) + mk.On("Bytes").Return(nil, errors.New("Failed to encode revocation public key")) + rk.SetKey(mk) + err := rk.Store() assert.Error(t, err, "Should fail if store is called with empty revocation public key") if err != nil { assert.Contains(t, err.Error(), "Failed to encode revocation public key") @@ -147,30 +146,37 @@ func TestStoreNilRevocationPublicKey(t *testing.T) { } func TestEncodeKeys(t *testing.T) { - idemix := cidemix.InstanceForCurve(cidemix.FP256BN) - privateKey, err := idemix.GenerateLongTermRevocationKey() + csp := getCSP(t) + + RevocationPrivateKey, err := csp.KeyGen(&bccsp.IdemixRevocationKeyGenOpts{Temporary: true}) if err != nil { t.Fatalf("Failed to generate ECDSA key for revocation authority") } - pkstr, pubkeystr, err := EncodeKeys(privateKey, &privateKey.PublicKey) + + RevocationPublicKey, err := RevocationPrivateKey.PublicKey() + if err != nil { + t.Fatalf("Failed to obtain public key") + } + + pkstr, pubkeystr, err := EncodeKeys(RevocationPrivateKey, RevocationPublicKey) assert.NoError(t, err) - _, _, err = DecodeKeys([]byte(""), pubkeystr) + _, _, err = DecodeKeys([]byte(""), pubkeystr, csp) assert.Error(t, err) - assert.Equal(t, "Failed to decode ECDSA private key", err.Error()) + assert.ErrorContains(t, err, "Failed to parse private key bytes") - _, _, err = DecodeKeys(pubkeystr, pkstr) + _, _, err = DecodeKeys(pubkeystr, pkstr, csp) assert.Error(t, err, "DecodeKeys should fail as encoded public key string is passed as private key") - _, _, err = DecodeKeys(pkstr, []byte("")) + _, _, err = DecodeKeys(pkstr, []byte(""), csp) assert.Error(t, err, "DecodeKeys should fail as empty string is passed for encoded public key string") - assert.Contains(t, err.Error(), "Failed to decode ECDSA public key") + assert.Contains(t, err.Error(), "Failed to parse public key bytes") - _, _, err = DecodeKeys(pkstr, pkstr) + _, _, err = DecodeKeys(pkstr, pkstr, csp) assert.Error(t, err, "DecodeKeys should fail as encoded private key string is passed as public key") - assert.Contains(t, err.Error(), "Failed to parse ECDSA public key bytes") + assert.Contains(t, err.Error(), "Failed to parse public key bytes") - privateKey1, pubKey, err := DecodeKeys(pkstr, pubkeystr) + privateKey1, pubKey, err := DecodeKeys(pkstr, pubkeystr, csp) assert.NoError(t, err) assert.NotNil(t, privateKey1) assert.NotNil(t, pubKey) @@ -178,17 +184,20 @@ func TestEncodeKeys(t *testing.T) { func TestStoreReadonlyRevocationPublicKeyFilepath(t *testing.T) { testdir := t.TempDir() - idemix := cidemix.InstanceForCurve(cidemix.FP256BN) privkeyfile, err := os.CreateTemp(testdir, DefaultRevocationPrivateKeyFile) if err != nil { t.Fatalf("Failed to create temp file: %s", err.Error()) } - key, err := idemix.GenerateLongTermRevocationKey() + RevocationPrivateKey, err := getCSP(t).KeyGen(&bccsp.IdemixRevocationKeyGenOpts{Temporary: true}) if err != nil { t.Fatalf("Failed to generate test revocation key: %s", err.Error()) } - privKey, _, err := EncodeKeys(key, &key.PublicKey) + RevocationPublicKey, err := RevocationPrivateKey.PublicKey() + if err != nil { + t.Fatalf("Failed to obtain public key") + } + privKey, _, err := EncodeKeys(RevocationPrivateKey, RevocationPublicKey) if err != nil { t.Fatalf("Failed to encode test revocation key: %s", err.Error()) } @@ -198,13 +207,13 @@ func TestStoreReadonlyRevocationPublicKeyFilepath(t *testing.T) { } pubkeyfile := path.Join(testdir, "testdata1/RevocationPublicKey") - idemixLib := new(mocks.Lib) err = os.MkdirAll(path.Dir(pubkeyfile), 4444) if err != nil { t.Fatalf("Failed to create read only directory: %s", err.Error()) } + idemixLib := getCSP(t) rk := NewRevocationKey(pubkeyfile, privkeyfile.Name(), idemixLib) - rk.SetKey(key) + rk.SetKey(RevocationPrivateKey) err = rk.Store() assert.Error(t, err, "Should fail if issuer public key is being stored to read-only directory") if err != nil { diff --git a/lib/server/idemix/utils_test.go b/lib/server/idemix/utils_test.go new file mode 100644 index 000000000..f264bd94c --- /dev/null +++ b/lib/server/idemix/utils_test.go @@ -0,0 +1,316 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package idemix_test + +import ( + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" + "time" + + idemix "github.com/IBM/idemix/bccsp" + "github.com/IBM/idemix/bccsp/schemes/dlog/crypto/translator/amcl" + "github.com/IBM/idemix/bccsp/types" + bccsp "github.com/IBM/idemix/bccsp/types" + ibccsp "github.com/IBM/idemix/bccsp/types" + math "github.com/IBM/mathlib" + "github.com/hyperledger/fabric-ca/api" + . "github.com/hyperledger/fabric-ca/lib/server/idemix" + "github.com/hyperledger/fabric-ca/lib/server/idemix/mocks" + dmocks "github.com/hyperledger/fabric-ca/lib/server/idemix/mocks" + "github.com/hyperledger/fabric-ca/util" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" +) + +func getIssuer(t *testing.T, testDir string, getranderror, newIssuerKeyerror bool) (*dmocks.DbFabricCADB, Issuer) { + err := os.MkdirAll(filepath.Join(testDir, "msp/keystore"), 0o777) + if err != nil { + t.Fatalf("Failed to create directory: %s", err.Error()) + } + + db := new(dmocks.DbFabricCADB) + + tx := new(dmocks.DbFabricCATx) + tx.On("Commit").Return(nil) + tx.On("Rollback").Return(nil) + tx.On("Rebind", SelectRAInfo).Return(SelectRAInfo) + tx.On("Rebind", UpdateNextHandle).Return(UpdateNextHandle) + tx.On("Exec", UpdateNextHandle, 2, 1).Return(nil, nil) + rcInfos := []RevocationAuthorityInfo{} + f1 := getTxSelectFunc(t, &rcInfos, 1, false, true) + tx.On("Select", &rcInfos, SelectRAInfo).Return(f1) + + db.On("BeginTx").Return(tx) + db.On("IsInitialized").Return(true) + + cfg := &Config{ + RHPoolSize: 100, + NonceExpiration: "15s", + NonceSweepInterval: "15m", + } + + revSK := new(mocks.BccspKey) + revPK := new(mocks.BccspKey) + revSK.On("PublicKey").Return(revPK, nil) + revSK.On("Bytes").Return([]byte("revSK_Bytes"), nil) + revPK.On("Bytes").Return([]byte("revPK_Bytes"), nil) + isk := new(mocks.BccspKey) + ipk := new(mocks.BccspKey) + isk.On("PublicKey").Return(ipk, nil) + isk.On("Bytes").Return([]byte("isk_Bytes"), nil) + ipk.On("Bytes").Return([]byte("ipk_Bytes"), nil) + + mockCsp := new(mocks.BccspBCCSP) + mockCsp.On("KeyGen", &types.IdemixIssuerKeyGenOpts{Temporary: true, AttributeNames: []string{"OU", "Role", "EnrollmentID", "RevocationHandle"}}). + Return(isk, nil) + + var issuer Issuer + if !getranderror && !newIssuerKeyerror { + issuer = NewIssuer("ca1", testDir, cfg, getCSP(t)) + } else { + if newIssuerKeyerror { + mockCsp.On("KeyGen", &types.IdemixRevocationKeyGenOpts{Temporary: true}).Return(nil, errors.New("error error")) + } else { + mockCsp.On("KeyGen", &types.IdemixRevocationKeyGenOpts{Temporary: true}).Return(revSK, nil) + } + issuer = NewIssuer("ca1", testDir, cfg, mockCsp) + } + + f := getSelectFunc(t, true, false) + + rcInfosForSelect := []RevocationAuthorityInfo{} + db.On("Select", "GetRAInfo", &rcInfosForSelect, SelectRAInfo).Return(f) + rcinfo := RevocationAuthorityInfo{ + Epoch: 1, + NextRevocationHandle: 1, + LastHandleInPool: 100, + Level: 1, + } + result := new(dmocks.SqlResult) + result.On("RowsAffected").Return(int64(1), nil) + db.On("NamedExec", "AddRAInfo", InsertRAInfo, &rcinfo).Return(result, nil) + + return db, issuer +} + +func getCredsSelectFunc(t *testing.T, creds *[]CredRecord, isAppend bool) func(string, interface{}, string, ...interface{}) error { + return func(funcName string, dest interface{}, query string, args ...interface{}) error { + credRecs := dest.(*[]CredRecord) + cred := CredRecord{ + ID: "foo", + Status: "active", + Cred: "", + } + + if isAppend { + //*creds = append(*creds, cred) + *credRecs = append(*credRecs, cred) + } + return nil + } +} + +func generatePublicPrivateKeyPair(t *testing.T) (string, string, string, error) { + tmpDir, err := os.MkdirTemp(os.TempDir(), strings.Replace(t.Name(), "/", "-", -1)) + assert.NoError(t, err) + + testPublicKeyFile := filepath.Join(tmpDir, "IdemixPublicKey") + testSecretKeyFile := filepath.Join(tmpDir, "IdemixSecretKey") + + pk, sk := makePubPrivKeyPair(t) + err = ioutil.WriteFile(testPublicKeyFile, pk, 0o644) + if err != nil { + t.Fatalf("Failed writing public key to file: %s", err.Error()) + } + + err = ioutil.WriteFile(testSecretKeyFile, sk, 0o644) + if err != nil { + t.Fatalf("Failed writing private key to file: %s", err.Error()) + } + return testPublicKeyFile, testSecretKeyFile, tmpDir, err +} + +func makePubPrivKeyPair(t *testing.T) ([]byte, []byte) { + attrs := []string{AttrOU, AttrRole, AttrEnrollmentID, AttrRevocationHandle} + + IssuerSecretKey, err := getCSP(t).KeyGen(&bccsp.IdemixIssuerKeyGenOpts{Temporary: true, AttributeNames: attrs}) + assert.NoError(t, err) + + IssuerPublicKey, err := IssuerSecretKey.PublicKey() + assert.NoError(t, err) + + iskBytes, err := IssuerSecretKey.Bytes() + assert.NoError(t, err) + + ipkBytes, err := IssuerPublicKey.Bytes() + assert.NoError(t, err) + + return ipkBytes, iskBytes +} + +func GeneratePublicPrivateKeyPair(t *testing.T) (string, string, string, error) { + tmpDir, err := os.MkdirTemp(os.TempDir(), strings.Replace(t.Name(), "/", "-", -1)) + assert.NoError(t, err) + + testPublicKeyFile := filepath.Join(tmpDir, "IdemixPublicKey") + testSecretKeyFile := filepath.Join(tmpDir, "IdemixSecretKey") + + pk, sk := makeIssuerKeypair(getCSP(t), t) + err = ioutil.WriteFile(testPublicKeyFile, pk, 0o644) + if err != nil { + t.Fatalf("Failed writing public key to file: %s", err.Error()) + } + + err = ioutil.WriteFile(testSecretKeyFile, sk, 0o644) + if err != nil { + t.Fatalf("Failed writing private key to file: %s", err.Error()) + } + return testPublicKeyFile, testSecretKeyFile, tmpDir, err +} + +// NewDummyKeyStore instantiate a dummy key store +// that neither loads nor stores keys +func NewDummyKeyStore() bccsp.KeyStore { + return &dummyKeyStore{} +} + +// dummyKeyStore is a read-only KeyStore that neither loads nor stores keys. +type dummyKeyStore struct { +} + +// ReadOnly returns true if this KeyStore is read only, false otherwise. +// If ReadOnly is true then StoreKey will fail. +func (ks *dummyKeyStore) ReadOnly() bool { + return true +} + +// GetKey returns a key object whose SKI is the one passed. +func (ks *dummyKeyStore) GetKey(ski []byte) (bccsp.Key, error) { + return nil, errors.New("Key not found. This is a dummy KeyStore") +} + +// StoreKey stores the key k in this KeyStore. +// If this KeyStore is read only then the method will fail. +func (ks *dummyKeyStore) StoreKey(k bccsp.Key) error { + return errors.New("Cannot store key. This is a dummy read-only KeyStore") +} + +func makeIssuerKeypair(CSP bccsp.BCCSP, t *testing.T) ([]byte, []byte) { + attrs := []string{AttrOU, AttrRole, AttrEnrollmentID, AttrRevocationHandle} + + IssuerSecretKey, err := CSP.KeyGen(&bccsp.IdemixIssuerKeyGenOpts{Temporary: true, AttributeNames: attrs}) + assert.NoError(t, err) + IssuerPublicKey, err := IssuerSecretKey.PublicKey() + assert.NoError(t, err) + + IssuerSecretKeyBytes, err := IssuerSecretKey.Bytes() + assert.NoError(t, err) + + IssuerPublicKeyBytes, err := IssuerPublicKey.Bytes() + assert.NoError(t, err) + + return IssuerPublicKeyBytes, IssuerSecretKeyBytes +} + +func getCSP(t *testing.T) bccsp.BCCSP { + curve := math.Curves[math.BLS12_381_BBS] + translator := &amcl.Gurvy{C: curve} + + CSP, err := idemix.New(NewDummyKeyStore(), curve, translator, true) + assert.NoError(t, err) + + return CSP +} + +func getCredSelectFunc(t *testing.T, isError bool) func(string, interface{}, string, ...interface{}) error { + return func(funcName string, dest interface{}, query string, args ...interface{}) error { + crs := dest.(*[]CredRecord) + cr := getCredRecord() + *crs = append(*crs, cr) + if isError { + return errors.New("Failed to get credentials from DB") + } + return nil + } +} + +func getCredRecord() CredRecord { + return CredRecord{ + ID: "foo", + CALabel: "", + Expiry: time.Now(), + Level: 1, + Reason: 0, + Status: "good", + RevocationHandle: "1", + Cred: "blah", + } +} + +func createCRI(t *testing.T) ([]byte, error) { + RevocationKey, err := getCSP(t).KeyGen(&bccsp.IdemixRevocationKeyGenOpts{Temporary: true}) + assert.NoError(t, err) + + cri, err := getCSP(t).Sign( + RevocationKey, + nil, + &bccsp.IdemixCRISignerOpts{}, + ) + assert.NoError(t, err) + + return cri, nil +} + +func getB64EncodedCred(cred []byte) (string, error) { + b64CredBytes := util.B64Encode(cred) + return b64CredBytes, nil +} + +func getReadBodyFunc(t *testing.T, credReq, nonce []byte) func(body interface{}) error { + return func(body interface{}) error { + enrollReq, _ := body.(*api.IdemixEnrollmentRequestNet) + if credReq == nil { + return errors.New("Error reading the body") + } + enrollReq.CredRequest = credReq + enrollReq.IssuerNonce = nonce + return nil + } +} + +func newIdemixCredentialRequest(t *testing.T, nonce []byte, testPublicKeyFile, testSecretKeyFile string) ([]byte, ibccsp.Key) { + csp := getCSP(t) + + issuerCred := NewIssuerCredential(testPublicKeyFile, testSecretKeyFile, csp) + err := issuerCred.Load() + if err != nil { + t.Fatalf("Failed to load issuer credential") + } + + isk, err := issuerCred.GetIssuerKey() + if err != nil { + t.Fatalf("Issuer credential returned error while getting issuer key") + } + + ipk, err := isk.PublicKey() + assert.NoError(t, err) + + UserKey, err := csp.KeyGen(&types.IdemixUserSecretKeyGenOpts{Temporary: true}) + assert.NoError(t, err) + + credRequest, err := csp.Sign( + UserKey, + nil, + &bccsp.IdemixCredentialRequestSignerOpts{IssuerPK: ipk, IssuerNonce: nonce}, + ) + assert.NoError(t, err) + + return credRequest, UserKey +} diff --git a/testdata/IdemixRevocationPrivateKey b/testdata/IdemixRevocationPrivateKey index 8255faf61..884775c01 100644 Binary files a/testdata/IdemixRevocationPrivateKey and b/testdata/IdemixRevocationPrivateKey differ diff --git a/testdata/IdemixRevocationPublicKey b/testdata/IdemixRevocationPublicKey index 5110c6f9d..e0e446eac 100644 --- a/testdata/IdemixRevocationPublicKey +++ b/testdata/IdemixRevocationPublicKey @@ -1,5 +1,5 @@ ------BEGIN PUBLIC KEY----- -MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEFGIuG2TbZTNwVs+c3rLaY5IB5s06Gh70 -d/PdBa/oyB+7b2gahOlq/0gyXZtrwwdrJtZbllqJqJQR/Wo6gaYusjwtMXI0QYK1 -HMxg738Y6c0WoIweB2nSnsqsSM7CG7Hx ------END PUBLIC KEY----- +-----BEGIN ECDSA Public Key----- +MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEdJM0WRtvQ1whhuUWcIYxYsIl9t2oe4PH +ttkzRqAEJUhq/HW+VAvHxH9Js+u0+1xrJf44fx8TRLHVlHlmrtQt5OktvTEP8K6a +RmU+DphXvWvNiWOAo70v3CcoswCz4lAo +-----END ECDSA Public Key----- diff --git a/vendor/github.com/IBM/idemix/bccsp/bccsp.go b/vendor/github.com/IBM/idemix/bccsp/bccsp.go new file mode 100644 index 000000000..0583c9dbe --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/bccsp.go @@ -0,0 +1,442 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package idemix + +import ( + "reflect" + + "github.com/IBM/idemix/bccsp/handlers" + "github.com/IBM/idemix/bccsp/schemes/aries" + "github.com/IBM/idemix/bccsp/schemes/dlog/bridge" + idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" + bccsp "github.com/IBM/idemix/bccsp/types" + math "github.com/IBM/mathlib" + "github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub" + "github.com/pkg/errors" +) + +type csp struct { + *CSP +} + +func New(keyStore bccsp.KeyStore, curve *math.Curve, translator idemix.Translator, exportable bool) (*csp, error) { + base, err := NewImpl(keyStore) + if err != nil { + return nil, errors.Wrap(err, "failed instantiating base bccsp") + } + + csp := &csp{CSP: base} + + idmx := &idemix.Idemix{ + Curve: curve, + Translator: translator, + } + + // key generators + base.AddWrapper(reflect.TypeOf(&bccsp.IdemixIssuerKeyGenOpts{}), + &handlers.IssuerKeyGen{ + Exportable: exportable, + Issuer: &bridge.Issuer{ + Idemix: idmx, Translator: translator, + }, + }) + base.AddWrapper(reflect.TypeOf(&bccsp.IdemixUserSecretKeyGenOpts{}), + &handlers.UserKeyGen{ + Exportable: exportable, + User: &bridge.User{ + Idemix: idmx, Translator: translator, + }, + }) + base.AddWrapper(reflect.TypeOf(&bccsp.IdemixRevocationKeyGenOpts{}), + &handlers.RevocationKeyGen{ + Exportable: exportable, + Revocation: &bridge.Revocation{ + Idemix: idmx, Translator: translator, + }, + }) + + // key derivers + base.AddWrapper(reflect.TypeOf(handlers.NewUserSecretKey(nil, true)), + &handlers.NymKeyDerivation{ + Exportable: exportable, + Translator: translator, + User: &bridge.User{ + Idemix: idmx, Translator: translator, + }, + }) + + // signers + base.AddWrapper(reflect.TypeOf(handlers.NewUserSecretKey(nil, true)), + &userSecreKeySignerMultiplexer{ + signer: &handlers.Signer{ + SignatureScheme: &bridge.SignatureScheme{ + Idemix: idmx, Translator: translator, + }}, + nymSigner: &handlers.NymSigner{ + NymSignatureScheme: &bridge.NymSignatureScheme{ + Idemix: idmx, Translator: translator, + }}, + credentialRequestSigner: &handlers.CredentialRequestSigner{ + CredRequest: &bridge.CredRequest{ + Idemix: idmx, Translator: translator, + }}, + }) + base.AddWrapper(reflect.TypeOf(handlers.NewIssuerSecretKey(nil, true)), + &handlers.CredentialSigner{ + Credential: &bridge.Credential{ + Idemix: idmx, Translator: translator, + }, + }) + base.AddWrapper(reflect.TypeOf(handlers.NewRevocationSecretKey(nil, true)), + &handlers.CriSigner{ + Revocation: &bridge.Revocation{ + Idemix: idmx, Translator: translator, + }, + }) + + // verifiers + base.AddWrapper(reflect.TypeOf(handlers.NewIssuerPublicKey(nil)), + &issuerPublicKeyVerifierMultiplexer{ + verifier: &handlers.Verifier{ + SignatureScheme: &bridge.SignatureScheme{ + Idemix: idmx, Translator: translator, + }}, + credentialRequestVerifier: &handlers.CredentialRequestVerifier{ + CredRequest: &bridge.CredRequest{ + Idemix: idmx, Translator: translator, + }}, + }) + base.AddWrapper(reflect.TypeOf(handlers.NewNymPublicKey(nil, translator)), + &handlers.NymVerifier{ + NymSignatureScheme: &bridge.NymSignatureScheme{ + Idemix: idmx, Translator: translator, + }, + }) + base.AddWrapper(reflect.TypeOf(handlers.NewUserSecretKey(nil, true)), + &handlers.CredentialVerifier{ + Credential: &bridge.Credential{ + Idemix: idmx, Translator: translator, + }, + }) + base.AddWrapper(reflect.TypeOf(handlers.NewRevocationPublicKey(nil)), + &handlers.CriVerifier{ + Revocation: &bridge.Revocation{ + Idemix: idmx, Translator: translator, + }, + }) + + // importers + base.AddWrapper(reflect.TypeOf(&bccsp.IdemixUserSecretKeyImportOpts{}), + &handlers.UserKeyImporter{ + Exportable: exportable, + User: &bridge.User{ + Idemix: idmx, Translator: translator, + }, + }) + base.AddWrapper(reflect.TypeOf(&bccsp.IdemixIssuerPublicKeyImportOpts{}), + &handlers.IssuerPublicKeyImporter{ + Issuer: &bridge.Issuer{ + Idemix: idmx, Translator: translator, + }, + }) + base.AddWrapper(reflect.TypeOf(&bccsp.IdemixIssuerKeyImportOpts{}), + &handlers.IssuerKeyImporter{ + Exportable: exportable, + Issuer: &bridge.Issuer{ + Idemix: idmx, Translator: translator, + }, + }) + base.AddWrapper(reflect.TypeOf(&bccsp.IdemixNymPublicKeyImportOpts{}), + &handlers.NymPublicKeyImporter{ + User: &bridge.User{ + Idemix: idmx, Translator: translator, + }, + Translator: translator, + }) + base.AddWrapper(reflect.TypeOf(&bccsp.IdemixNymKeyImportOpts{}), + &handlers.NymKeyImporter{ + Exportable: exportable, + User: &bridge.User{ + Idemix: idmx, Translator: translator, + }, + Translator: translator, + }) + base.AddWrapper(reflect.TypeOf(&bccsp.IdemixRevocationPublicKeyImportOpts{}), + &handlers.RevocationPublicKeyImporter{}) + base.AddWrapper(reflect.TypeOf(&bccsp.IdemixRevocationKeyImportOpts{}), + &handlers.RevocationKeyImporter{ + Exportable: exportable, + Revocation: &bridge.Revocation{ + Idemix: idmx, Translator: translator, + }, + }) + + return csp, nil +} + +func NewAries(keyStore bccsp.KeyStore, curve *math.Curve, _translator idemix.Translator, exportable bool) (*csp, error) { + base, err := NewImpl(keyStore) + if err != nil { + return nil, errors.Wrap(err, "failed instantiating base bccsp") + } + + csp := &csp{CSP: base} + + rng, err := curve.Rand() + if err != nil { + return nil, errors.Wrap(err, "failed instantiating PRNG") + } + + // key generators + base.AddWrapper(reflect.TypeOf(&bccsp.IdemixIssuerKeyGenOpts{}), + &handlers.IssuerKeyGen{ + Exportable: exportable, + Issuer: &aries.Issuer{}, + }) + base.AddWrapper(reflect.TypeOf(&bccsp.IdemixUserSecretKeyGenOpts{}), + &handlers.UserKeyGen{ + Exportable: exportable, + User: &aries.User{ + Curve: curve, + Rng: rng, + }, + }) + base.AddWrapper(reflect.TypeOf(&bccsp.IdemixRevocationKeyGenOpts{}), + &handlers.RevocationKeyGen{ + Exportable: exportable, + Revocation: &aries.RevocationAuthority{ + Curve: curve, + Rng: rng, + }, + }) + + // key derivers + base.AddWrapper(reflect.TypeOf(handlers.NewUserSecretKey(nil, true)), + &handlers.NymKeyDerivation{ + Exportable: exportable, + Translator: _translator, + User: &aries.User{ + Curve: curve, + Rng: rng, + }, + }) + + // signers + base.AddWrapper(reflect.TypeOf(handlers.NewUserSecretKey(nil, true)), + &userSecreKeySignerMultiplexer{ + signer: &handlers.Signer{ + SignatureScheme: &aries.Signer{ + Curve: curve, + Rng: rng, + }}, + nymSigner: &handlers.NymSigner{ + NymSignatureScheme: &aries.NymSigner{ + Curve: curve, + Rng: rng, + }}, + blindCredentialRequestSigner: &handlers.BlindCredentialRequestSigner{ + CredRequest: &aries.CredRequest{ + Curve: curve, + }}, + credentialRequestSigner: nil, // aries does not implement this approach + }) + base.AddWrapper(reflect.TypeOf(handlers.NewIssuerSecretKey(nil, true)), + &handlers.CredentialSigner{ + Credential: &aries.Cred{ + Curve: curve, + Bls: bbs12381g2pub.New(), + }, + }) + base.AddWrapper(reflect.TypeOf(handlers.NewRevocationSecretKey(nil, true)), + &handlers.CriSigner{ + Revocation: &aries.RevocationAuthority{ + Curve: curve, + Rng: rng, + }, + }) + + // verifiers + base.AddWrapper(reflect.TypeOf(handlers.NewIssuerPublicKey(nil)), + &issuerPublicKeyVerifierMultiplexer{ + verifier: &handlers.Verifier{ + SignatureScheme: &aries.Signer{ + Curve: curve, + Rng: rng, + }}, + blindcredentialRequestVerifier: &handlers.BlindCredentialRequestVerifier{ + CredRequest: &aries.CredRequest{ + Curve: curve, + }}, + credentialRequestVerifier: nil, // aries does not implement this type of issuance + }) + base.AddWrapper(reflect.TypeOf(handlers.NewNymPublicKey(nil, _translator)), + &handlers.NymVerifier{ + NymSignatureScheme: &aries.NymSigner{ + Curve: curve, + Rng: rng, + }, + }) + base.AddWrapper(reflect.TypeOf(handlers.NewUserSecretKey(nil, true)), + &handlers.CredentialVerifier{ + Credential: &aries.Cred{ + Curve: curve, + Bls: bbs12381g2pub.New(), + }, + }) + base.AddWrapper(reflect.TypeOf(handlers.NewRevocationPublicKey(nil)), + &handlers.CriVerifier{ + Revocation: &aries.RevocationAuthority{ + Curve: curve, + Rng: rng, + }, + }) + + // importers + base.AddWrapper(reflect.TypeOf(&bccsp.IdemixUserSecretKeyImportOpts{}), + &handlers.UserKeyImporter{ + Exportable: exportable, + User: &aries.User{ + Curve: curve, + Rng: rng, + }, + }) + base.AddWrapper(reflect.TypeOf(&bccsp.IdemixIssuerPublicKeyImportOpts{}), + &handlers.IssuerPublicKeyImporter{ + Issuer: &aries.Issuer{}, + }) + base.AddWrapper(reflect.TypeOf(&bccsp.IdemixIssuerKeyImportOpts{}), + &handlers.IssuerKeyImporter{ + Exportable: exportable, + Issuer: &aries.Issuer{}, + }) + base.AddWrapper(reflect.TypeOf(&bccsp.IdemixNymPublicKeyImportOpts{}), + &handlers.NymPublicKeyImporter{ + User: &aries.User{ + Curve: curve, + Rng: rng, + }, + Translator: _translator, + }) + base.AddWrapper(reflect.TypeOf(&bccsp.IdemixNymKeyImportOpts{}), + &handlers.NymKeyImporter{ + Exportable: exportable, + User: &aries.User{ + Curve: curve, + Rng: rng, + }, + Translator: _translator, + }) + base.AddWrapper(reflect.TypeOf(&bccsp.IdemixRevocationPublicKeyImportOpts{}), + &handlers.RevocationPublicKeyImporter{}) + base.AddWrapper(reflect.TypeOf(&bccsp.IdemixRevocationKeyImportOpts{}), + &handlers.RevocationKeyImporter{ + Exportable: exportable, + Revocation: &aries.RevocationAuthority{ + Curve: curve, + Rng: rng, + }, + }) + + return csp, nil +} + +// Sign signs digest using key k. +// The opts argument should be appropriate for the primitive used. +// +// Note that when a signature of a hash of a larger message is needed, +// the caller is responsible for hashing the larger message and passing +// the hash (as digest). +// Notice that this is overriding the Sign methods of the sw impl. to avoid the digest check. +func (csp *csp) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error) { + // Validate arguments + if k == nil { + return nil, errors.New("Invalid Key. It must not be nil.") + } + // Do not check for digest + + keyType := reflect.TypeOf(k) + signer, found := csp.Signers[keyType] + if !found { + return nil, errors.Errorf("Unsupported 'SignKey' provided [%s]", keyType) + } + + signature, err = signer.Sign(k, digest, opts) + if err != nil { + return nil, errors.Wrapf(err, "Failed signing with opts [%v]", opts) + } + + return +} + +// Verify verifies signature against key k and digest +// Notice that this is overriding the Sign methods of the sw impl. to avoid the digest check. +func (csp *csp) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) { + // Validate arguments + if k == nil { + return false, errors.New("Invalid Key. It must not be nil.") + } + if len(signature) == 0 { + return false, errors.New("Invalid signature. Cannot be empty.") + } + // Do not check for digest + + verifier, found := csp.Verifiers[reflect.TypeOf(k)] + if !found { + return false, errors.Errorf("Unsupported 'VerifyKey' provided [%v]", k) + } + + valid, err = verifier.Verify(k, signature, digest, opts) + if err != nil { + return false, errors.Wrapf(err, "Failed verifing with opts [%v]", opts) + } + + return +} + +type userSecreKeySignerMultiplexer struct { + signer *handlers.Signer + nymSigner *handlers.NymSigner + credentialRequestSigner *handlers.CredentialRequestSigner + blindCredentialRequestSigner *handlers.BlindCredentialRequestSigner +} + +func (s *userSecreKeySignerMultiplexer) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error) { + switch opts.(type) { + case *bccsp.IdemixSignerOpts: + return s.signer.Sign(k, digest, opts) + case *bccsp.IdemixNymSignerOpts: + return s.nymSigner.Sign(k, digest, opts) + case *bccsp.IdemixCredentialRequestSignerOpts: + return s.credentialRequestSigner.Sign(k, digest, opts) + case *bccsp.IdemixBlindCredentialRequestSignerOpts: + return s.blindCredentialRequestSigner.Sign(k, digest, opts) + default: + return nil, errors.New("invalid opts, expected *bccsp.IdemixSignerOpt or *bccsp.IdemixNymSignerOpts or *bccsp.IdemixCredentialRequestSignerOpts") + } +} + +type issuerPublicKeyVerifierMultiplexer struct { + verifier *handlers.Verifier + credentialRequestVerifier *handlers.CredentialRequestVerifier + blindcredentialRequestVerifier *handlers.BlindCredentialRequestVerifier +} + +func (v *issuerPublicKeyVerifierMultiplexer) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) { + switch opts.(type) { + case *bccsp.EidNymAuditOpts: + return v.verifier.AuditNymEid(k, signature, digest, opts) + case *bccsp.RhNymAuditOpts: + return v.verifier.AuditNymRh(k, signature, digest, opts) + case *bccsp.IdemixSignerOpts: + return v.verifier.Verify(k, signature, digest, opts) + case *bccsp.IdemixCredentialRequestSignerOpts: + return v.credentialRequestVerifier.Verify(k, signature, digest, opts) + case *bccsp.IdemixBlindCredentialRequestSignerOpts: + return v.blindcredentialRequestVerifier.Verify(k, signature, digest, opts) + default: + return false, errors.New("invalid opts, expected *bccsp.IdemixSignerOpts or *bccsp.IdemixCredentialRequestSignerOpts") + } +} diff --git a/vendor/github.com/IBM/idemix/bccsp/handlers/cred.go b/vendor/github.com/IBM/idemix/bccsp/handlers/cred.go new file mode 100644 index 000000000..740a456bb --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/handlers/cred.go @@ -0,0 +1,169 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package handlers + +import ( + "github.com/IBM/idemix/bccsp/types" + bccsp "github.com/IBM/idemix/bccsp/types" + "github.com/pkg/errors" +) + +// CredentialRequestSigner produces credential requests +type CredentialRequestSigner struct { + // CredRequest implements the underlying cryptographic algorithms + CredRequest types.CredRequest +} + +func (c *CredentialRequestSigner) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) ([]byte, error) { + userSecretKey, ok := k.(*UserSecretKey) + if !ok { + return nil, errors.New("invalid key, expected *userSecretKey") + } + credentialRequestSignerOpts, ok := opts.(*bccsp.IdemixCredentialRequestSignerOpts) + if !ok { + return nil, errors.New("invalid options, expected *IdemixCredentialRequestSignerOpts") + } + if credentialRequestSignerOpts.IssuerPK == nil { + return nil, errors.New("invalid options, missing issuer public key") + } + issuerPK, ok := credentialRequestSignerOpts.IssuerPK.(*issuerPublicKey) + if !ok { + return nil, errors.New("invalid options, expected IssuerPK as *issuerPublicKey") + } + + return c.CredRequest.Sign(userSecretKey.Sk, issuerPK.pk, credentialRequestSignerOpts.IssuerNonce) +} + +type BlindCredentialRequestSigner struct { + CredRequest types.BlindCredRequest +} + +func (c *BlindCredentialRequestSigner) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) ([]byte, error) { + userSecretKey, ok := k.(*UserSecretKey) + if !ok { + return nil, errors.New("invalid key, expected *userSecretKey") + } + credentialRequestSignerOpts, ok := opts.(*bccsp.IdemixBlindCredentialRequestSignerOpts) + if !ok { + return nil, errors.New("invalid options, expected *IdemixCredentialRequestSignerOpts") + } + if credentialRequestSignerOpts.IssuerPK == nil { + return nil, errors.New("invalid options, missing issuer public key") + } + issuerPK, ok := credentialRequestSignerOpts.IssuerPK.(*issuerPublicKey) + if !ok { + return nil, errors.New("invalid options, expected IssuerPK as *issuerPublicKey") + } + + cred, blinding, err := c.CredRequest.Blind(userSecretKey.Sk, issuerPK.pk, credentialRequestSignerOpts.IssuerNonce) + if err != nil { + return nil, err + } + + credentialRequestSignerOpts.Blinding = blinding + + return cred, err +} + +// CredentialRequestVerifier verifies credential requests +type CredentialRequestVerifier struct { + // CredRequest implements the underlying cryptographic algorithms + CredRequest types.CredRequest +} + +func (c *CredentialRequestVerifier) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (bool, error) { + issuerPublicKey, ok := k.(*issuerPublicKey) + if !ok { + return false, errors.New("invalid key, expected *issuerPublicKey") + } + credentialRequestSignerOpts, ok := opts.(*bccsp.IdemixCredentialRequestSignerOpts) + if !ok { + return false, errors.New("invalid options, expected *IdemixCredentialRequestSignerOpts") + } + + err := c.CredRequest.Verify(signature, issuerPublicKey.pk, credentialRequestSignerOpts.IssuerNonce) + if err != nil { + return false, err + } + + return true, nil +} + +type BlindCredentialRequestVerifier struct { + CredRequest types.BlindCredRequest +} + +func (c *BlindCredentialRequestVerifier) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (bool, error) { + issuerPublicKey, ok := k.(*issuerPublicKey) + if !ok { + return false, errors.New("invalid key, expected *issuerPublicKey") + } + credentialRequestSignerOpts, ok := opts.(*bccsp.IdemixBlindCredentialRequestSignerOpts) + if !ok { + return false, errors.New("invalid options, expected *IdemixCredentialRequestSignerOpts") + } + + err := c.CredRequest.BlindVerify(signature, issuerPublicKey.pk, credentialRequestSignerOpts.IssuerNonce) + if err != nil { + return false, err + } + + return true, nil +} + +type CredentialSigner struct { + Credential types.Credential +} + +func (s *CredentialSigner) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error) { + issuerSecretKey, ok := k.(*issuerSecretKey) + if !ok { + return nil, errors.New("invalid key, expected *issuerSecretKey") + } + credOpts, ok := opts.(*bccsp.IdemixCredentialSignerOpts) + if !ok { + return nil, errors.New("invalid options, expected *IdemixCredentialSignerOpts") + } + + signature, err = s.Credential.Sign(issuerSecretKey.sk, digest, credOpts.Attributes) + if err != nil { + return nil, err + } + + return +} + +type CredentialVerifier struct { + Credential types.Credential +} + +func (v *CredentialVerifier) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) { + userSecretKey, ok := k.(*UserSecretKey) + if !ok { + return false, errors.New("invalid key, expected *userSecretKey") + } + credOpts, ok := opts.(*bccsp.IdemixCredentialSignerOpts) + if !ok { + return false, errors.New("invalid options, expected *IdemixCredentialSignerOpts") + } + if credOpts.IssuerPK == nil { + return false, errors.New("invalid options, missing issuer public key") + } + ipk, ok := credOpts.IssuerPK.(*issuerPublicKey) + if !ok { + return false, errors.New("invalid issuer public key, expected *issuerPublicKey") + } + if len(signature) == 0 { + return false, errors.New("invalid signature, it must not be empty") + } + + err = v.Credential.Verify(userSecretKey.Sk, ipk.pk, signature, credOpts.Attributes) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/vendor/github.com/IBM/idemix/bccsp/handlers/issuer.go b/vendor/github.com/IBM/idemix/bccsp/handlers/issuer.go new file mode 100644 index 000000000..bf9252799 --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/handlers/issuer.go @@ -0,0 +1,172 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package handlers + +import ( + "github.com/IBM/idemix/bccsp/types" + bccsp "github.com/IBM/idemix/bccsp/types" + "github.com/pkg/errors" +) + +// issuerSecretKey contains the issuer secret key +// and implements the bccsp.Key interface +type issuerSecretKey struct { + // sk is the idemix reference to the issuer key + sk types.IssuerSecretKey + // exportable if true, sk can be exported via the Bytes function + exportable bool +} + +func NewIssuerSecretKey(sk types.IssuerSecretKey, exportable bool) *issuerSecretKey { + return &issuerSecretKey{sk: sk, exportable: exportable} +} + +func (k *issuerSecretKey) Bytes() ([]byte, error) { + if k.exportable { + return k.sk.Bytes() + } + + return nil, errors.New("not exportable") +} + +func (k *issuerSecretKey) SKI() []byte { + pk, err := k.PublicKey() + if err != nil { + return nil + } + + return pk.SKI() +} + +func (*issuerSecretKey) Symmetric() bool { + return false +} + +func (*issuerSecretKey) Private() bool { + return true +} + +func (k *issuerSecretKey) PublicKey() (bccsp.Key, error) { + return &issuerPublicKey{k.sk.Public()}, nil +} + +// issuerPublicKey contains the issuer public key +// and implements the bccsp.Key interface +type issuerPublicKey struct { + pk types.IssuerPublicKey +} + +func NewIssuerPublicKey(pk types.IssuerPublicKey) *issuerPublicKey { + return &issuerPublicKey{pk} +} + +func (k *issuerPublicKey) Bytes() ([]byte, error) { + return k.pk.Bytes() +} + +func (k *issuerPublicKey) SKI() []byte { + return k.pk.Hash() +} + +func (*issuerPublicKey) Symmetric() bool { + return false +} + +func (*issuerPublicKey) Private() bool { + return false +} + +func (k *issuerPublicKey) PublicKey() (bccsp.Key, error) { + return k, nil +} + +// IssuerKeyGen generates issuer secret keys. +type IssuerKeyGen struct { + // exportable is a flag to allow an issuer secret key to be marked as exportable. + // If a secret key is marked as exportable, its Bytes method will return the key's byte representation. + Exportable bool + // Issuer implements the underlying cryptographic algorithms + Issuer types.Issuer +} + +func (g *IssuerKeyGen) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) { + o, ok := opts.(*bccsp.IdemixIssuerKeyGenOpts) + if !ok { + return nil, errors.New("invalid options, expected *bccsp.IdemixIssuerKeyGenOpts") + } + + // Create a new key pair + key, err := g.Issuer.NewKey(o.AttributeNames) + if err != nil { + return nil, err + } + + return &issuerSecretKey{exportable: g.Exportable, sk: key}, nil +} + +// IssuerPublicKeyImporter imports issuer public keys +type IssuerPublicKeyImporter struct { + // Issuer implements the underlying cryptographic algorithms + Issuer types.Issuer +} + +func (i *IssuerPublicKeyImporter) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.Key, err error) { + der, ok := raw.([]byte) + if !ok { + return nil, errors.New("invalid raw, expected byte array") + } + + if len(der) == 0 { + return nil, errors.New("invalid raw, it must not be nil") + } + + o, ok := opts.(*bccsp.IdemixIssuerPublicKeyImportOpts) + if !ok { + return nil, errors.New("invalid options, expected *bccsp.IdemixIssuerPublicKeyImportOpts") + } + + pk, err := i.Issuer.NewPublicKeyFromBytes(raw.([]byte), o.AttributeNames) + if err != nil { + return nil, err + } + + return &issuerPublicKey{pk}, nil +} + +// IssuerKeyImporter imports issuer public keys +type IssuerKeyImporter struct { + // exportable is a flag to allow an issuer secret key to be marked as exportable. + // If a secret key is marked as exportable, its Bytes method will return the key's byte representation. + Exportable bool + // Issuer implements the underlying cryptographic algorithms + Issuer types.Issuer +} + +func (i *IssuerKeyImporter) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.Key, err error) { + der, ok := raw.([]byte) + if !ok { + return nil, errors.New("invalid raw, expected byte array") + } + + if len(der) == 0 { + return nil, errors.New("invalid raw, it must not be nil") + } + + o, ok := opts.(*bccsp.IdemixIssuerKeyImportOpts) + if !ok { + return nil, errors.New("invalid options, expected *bccsp.IdemixIssuerKeyImportOpts") + } + + sk, err := i.Issuer.NewKeyFromBytes(raw.([]byte), o.AttributeNames) + if err != nil { + return nil, err + } + + return &issuerSecretKey{ + sk: sk, + exportable: i.Exportable, + }, nil +} diff --git a/vendor/github.com/IBM/idemix/bccsp/handlers/nym.go b/vendor/github.com/IBM/idemix/bccsp/handlers/nym.go new file mode 100644 index 000000000..6c35fa41c --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/handlers/nym.go @@ -0,0 +1,194 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package handlers + +import ( + "crypto/sha256" + + idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" + "github.com/IBM/idemix/bccsp/types" + bccsp "github.com/IBM/idemix/bccsp/types" + math "github.com/IBM/mathlib" + "github.com/pkg/errors" +) + +// NymSecretKey contains the nym secret key +type NymSecretKey struct { + // SKI of this key + Ski []byte + // Sk is the idemix reference to the nym secret + Sk *math.Zr + // Pk is the idemix reference to the nym public part + Pk *math.G1 + // Exportable if true, sk can be exported via the Bytes function + Exportable bool + + Translator idemix.Translator +} + +func computeSKI(serialise func() []byte) []byte { + raw := serialise() + + hash := sha256.New() + hash.Write(raw) + return hash.Sum(nil) + +} + +func NewNymSecretKey(sk *math.Zr, pk *math.G1, translator idemix.Translator, exportable bool) (*NymSecretKey, error) { + ski := computeSKI(sk.Bytes) + return &NymSecretKey{Ski: ski, Sk: sk, Pk: pk, Exportable: exportable, Translator: translator}, nil +} + +func (k *NymSecretKey) Bytes() ([]byte, error) { + if k.Exportable { + return k.Sk.Bytes(), nil + } + + return nil, errors.New("not supported") +} + +func (k *NymSecretKey) SKI() []byte { + c := make([]byte, len(k.Ski)) + copy(c, k.Ski) + return c +} + +func (*NymSecretKey) Symmetric() bool { + return false +} + +func (*NymSecretKey) Private() bool { + return true +} + +func (k *NymSecretKey) PublicKey() (bccsp.Key, error) { + ski := computeSKI(k.Pk.Bytes) + return &nymPublicKey{ski: ski, pk: k.Pk, translator: k.Translator}, nil +} + +type nymPublicKey struct { + // SKI of this key + ski []byte + // pk is the idemix reference to the nym public part + pk *math.G1 + + translator idemix.Translator +} + +func NewNymPublicKey(pk *math.G1, translator idemix.Translator) *nymPublicKey { + return &nymPublicKey{pk: pk, translator: translator} +} + +func (k *nymPublicKey) Bytes() ([]byte, error) { + ecp := k.translator.G1ToProto(k.pk) + return append(ecp.X, ecp.Y...), nil +} + +func (k *nymPublicKey) SKI() []byte { + return computeSKI(k.pk.Bytes) +} + +func (*nymPublicKey) Symmetric() bool { + return false +} + +func (*nymPublicKey) Private() bool { + return false +} + +func (k *nymPublicKey) PublicKey() (bccsp.Key, error) { + return k, nil +} + +// NymKeyDerivation derives nyms +type NymKeyDerivation struct { + // Exportable is a flag to allow an issuer secret key to be marked as Exportable. + // If a secret key is marked as Exportable, its Bytes method will return the key's byte representation. + Exportable bool + // User implements the underlying cryptographic algorithms + User types.User + + Translator idemix.Translator +} + +func (kd *NymKeyDerivation) KeyDeriv(k bccsp.Key, opts bccsp.KeyDerivOpts) (dk bccsp.Key, err error) { + userSecretKey, ok := k.(*UserSecretKey) + if !ok { + return nil, errors.New("invalid key, expected *userSecretKey") + } + nymKeyDerivationOpts, ok := opts.(*bccsp.IdemixNymKeyDerivationOpts) + if !ok { + return nil, errors.New("invalid options, expected *IdemixNymKeyDerivationOpts") + } + if nymKeyDerivationOpts.IssuerPK == nil { + return nil, errors.New("invalid options, missing issuer public key") + } + issuerPK, ok := nymKeyDerivationOpts.IssuerPK.(*issuerPublicKey) + if !ok { + return nil, errors.New("invalid options, expected IssuerPK as *issuerPublicKey") + } + + Nym, RandNym, err := kd.User.MakeNym(userSecretKey.Sk, issuerPK.pk) + if err != nil { + return nil, err + } + + return NewNymSecretKey(RandNym, Nym, kd.Translator, kd.Exportable) +} + +// NymPublicKeyImporter imports nym public keys +type NymPublicKeyImporter struct { + // User implements the underlying cryptographic algorithms + User types.User + + Translator idemix.Translator +} + +func (i *NymPublicKeyImporter) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.Key, err error) { + bytes, ok := raw.([]byte) + if !ok { + return nil, errors.New("invalid raw, expected byte array") + } + + if len(bytes) == 0 { + return nil, errors.New("invalid raw, it must not be nil") + } + + pk, err := i.User.NewPublicNymFromBytes(bytes) + if err != nil { + return nil, err + } + + return &nymPublicKey{pk: pk, translator: i.Translator}, nil +} + +// NymKeyImporter imports nym public keys +type NymKeyImporter struct { + Exportable bool + // User implements the underlying cryptographic algorithms + User types.User + + Translator idemix.Translator +} + +func (i *NymKeyImporter) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.Key, err error) { + bytes, ok := raw.([]byte) + if !ok { + return nil, errors.New("invalid raw, expected byte array") + } + + if len(bytes) == 0 { + return nil, errors.New("invalid raw, it must not be nil") + } + + pk, sk, err := i.User.NewNymFromBytes(bytes) + if err != nil { + return nil, err + } + + return NewNymSecretKey(sk, pk, i.Translator, i.Exportable) +} diff --git a/vendor/github.com/IBM/idemix/bccsp/handlers/nymsigner.go b/vendor/github.com/IBM/idemix/bccsp/handlers/nymsigner.go new file mode 100644 index 000000000..f62068d6b --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/handlers/nymsigner.go @@ -0,0 +1,96 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package handlers + +import ( + "github.com/IBM/idemix/bccsp/types" + bccsp "github.com/IBM/idemix/bccsp/types" + "github.com/pkg/errors" +) + +type NymSigner struct { + NymSignatureScheme types.NymSignatureScheme +} + +func (s *NymSigner) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) ([]byte, error) { + userSecretKey, ok := k.(*UserSecretKey) + if !ok { + return nil, errors.New("invalid key, expected *userSecretKey") + } + + signerOpts, ok := opts.(*bccsp.IdemixNymSignerOpts) + if !ok { + return nil, errors.New("invalid options, expected *IdemixNymSignerOpts") + } + + // Issuer public key + if signerOpts.IssuerPK == nil { + return nil, errors.New("invalid options, missing issuer public key") + } + ipk, ok := signerOpts.IssuerPK.(*issuerPublicKey) + if !ok { + return nil, errors.New("invalid issuer public key, expected *issuerPublicKey") + } + + // Nym + if signerOpts.Nym == nil { + return nil, errors.New("invalid options, missing nym key") + } + nymSk, ok := signerOpts.Nym.(*NymSecretKey) + if !ok { + return nil, errors.New("invalid nym key, expected *nymSecretKey") + } + + sigma, err := s.NymSignatureScheme.Sign( + userSecretKey.Sk, + nymSk.Pk, nymSk.Sk, + ipk.pk, + digest) + if err != nil { + return nil, err + } + + return sigma, nil +} + +type NymVerifier struct { + NymSignatureScheme types.NymSignatureScheme +} + +func (v *NymVerifier) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (bool, error) { + nymPublicKey, ok := k.(*nymPublicKey) + if !ok { + return false, errors.New("invalid key, expected *nymPublicKey") + } + + signerOpts, ok := opts.(*bccsp.IdemixNymSignerOpts) + if !ok { + return false, errors.New("invalid options, expected *IdemixNymSignerOpts") + } + + if signerOpts.IssuerPK == nil { + return false, errors.New("invalid options, missing issuer public key") + } + ipk, ok := signerOpts.IssuerPK.(*issuerPublicKey) + if !ok { + return false, errors.New("invalid issuer public key, expected *issuerPublicKey") + } + + if len(signature) == 0 { + return false, errors.New("invalid signature, it must not be empty") + } + + err := v.NymSignatureScheme.Verify( + ipk.pk, + nymPublicKey.pk, + signature, + digest) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/vendor/github.com/IBM/idemix/bccsp/handlers/revocation.go b/vendor/github.com/IBM/idemix/bccsp/handlers/revocation.go new file mode 100644 index 000000000..ad75f2adb --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/handlers/revocation.go @@ -0,0 +1,250 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package handlers + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/sha256" + "crypto/x509" + "encoding/pem" + "fmt" + "reflect" + + "github.com/IBM/idemix/bccsp/types" + bccsp "github.com/IBM/idemix/bccsp/types" + "github.com/pkg/errors" +) + +// revocationSecretKey contains the revocation secret key +// and implements the bccsp.Key interface +type revocationSecretKey struct { + // sk is the idemix reference to the revocation key + privKey *ecdsa.PrivateKey + // exportable if true, sk can be exported via the Bytes function + exportable bool +} + +func NewRevocationSecretKey(sk *ecdsa.PrivateKey, exportable bool) *revocationSecretKey { + return &revocationSecretKey{privKey: sk, exportable: exportable} +} + +// Bytes converts this key to its byte representation, +// if this operation is allowed. +func (k *revocationSecretKey) Bytes() ([]byte, error) { + if k.exportable { + return k.privKey.D.Bytes(), nil + } + + return nil, errors.New("not exportable") +} + +// SKI returns the subject key identifier of this key. +func (k *revocationSecretKey) SKI() []byte { + // Marshall the public key + raw := elliptic.Marshal(k.privKey.Curve, k.privKey.PublicKey.X, k.privKey.PublicKey.Y) + + // Hash it + hash := sha256.New() + hash.Write(raw) + return hash.Sum(nil) +} + +// Symmetric returns true if this key is a symmetric key, +// false if this key is asymmetric +func (k *revocationSecretKey) Symmetric() bool { + return false +} + +// Private returns true if this key is a private key, +// false otherwise. +func (k *revocationSecretKey) Private() bool { + return true +} + +// PublicKey returns the corresponding public key part of an asymmetric public/private key pair. +// This method returns an error in symmetric key schemes. +func (k *revocationSecretKey) PublicKey() (bccsp.Key, error) { + return &revocationPublicKey{&k.privKey.PublicKey}, nil +} + +type revocationPublicKey struct { + pubKey *ecdsa.PublicKey +} + +func NewRevocationPublicKey(pubKey *ecdsa.PublicKey) *revocationPublicKey { + return &revocationPublicKey{pubKey: pubKey} +} + +// Bytes converts this key to its byte representation, +// if this operation is allowed. +func (k *revocationPublicKey) Bytes() (raw []byte, err error) { + raw, err = x509.MarshalPKIXPublicKey(k.pubKey) + if err != nil { + return nil, fmt.Errorf("Failed marshalling key [%s]", err) + } + return +} + +// SKI returns the subject key identifier of this key. +func (k *revocationPublicKey) SKI() []byte { + // Marshall the public key + raw := elliptic.Marshal(k.pubKey.Curve, k.pubKey.X, k.pubKey.Y) + + // Hash it + hash := sha256.New() + hash.Write(raw) + return hash.Sum(nil) +} + +// Symmetric returns true if this key is a symmetric key, +// false if this key is asymmetric +func (k *revocationPublicKey) Symmetric() bool { + return false +} + +// Private returns true if this key is a private key, +// false otherwise. +func (k *revocationPublicKey) Private() bool { + return false +} + +// PublicKey returns the corresponding public key part of an asymmetric public/private key pair. +// This method returns an error in symmetric key schemes. +func (k *revocationPublicKey) PublicKey() (bccsp.Key, error) { + return k, nil +} + +// RevocationKeyGen generates revocation secret keys. +type RevocationKeyGen struct { + // exportable is a flag to allow an revocation secret key to be marked as exportable. + // If a secret key is marked as exportable, its Bytes method will return the key's byte representation. + Exportable bool + // Revocation implements the underlying cryptographic algorithms + Revocation types.Revocation +} + +func (g *RevocationKeyGen) KeyGen(opts bccsp.KeyGenOpts) (bccsp.Key, error) { + // Create a new key pair + key, err := g.Revocation.NewKey() + if err != nil { + return nil, err + } + + return &revocationSecretKey{exportable: g.Exportable, privKey: key}, nil +} + +// RevocationPublicKeyImporter imports revocation public key +type RevocationPublicKeyImporter struct { +} + +func (i *RevocationPublicKeyImporter) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.Key, err error) { + der, ok := raw.([]byte) + if !ok { + return nil, errors.New("invalid raw, expected byte array") + } + + if len(der) == 0 { + return nil, errors.New("invalid raw, it must not be nil") + } + + blockPub, _ := pem.Decode(raw.([]byte)) + if blockPub == nil { + return nil, errors.New("Failed to decode revocation ECDSA public key") + } + revocationPk, err := x509.ParsePKIXPublicKey(blockPub.Bytes) + if err != nil { + return nil, errors.Wrap(err, "Failed to parse revocation ECDSA public key bytes") + } + ecdsaPublicKey, isECDSA := revocationPk.(*ecdsa.PublicKey) + if !isECDSA { + return nil, errors.Errorf("key is of type %v, not of type ECDSA", reflect.TypeOf(revocationPk)) + } + + return &revocationPublicKey{ecdsaPublicKey}, nil +} + +// RevocationKeyImporter imports revocation key +type RevocationKeyImporter struct { + // exportable is a flag to allow an revocation secret key to be marked as exportable. + // If a secret key is marked as exportable, its Bytes method will return the key's byte representation. + Exportable bool + // Revocation implements the underlying cryptographic algorithms + Revocation types.Revocation +} + +func (i *RevocationKeyImporter) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.Key, err error) { + der, ok := raw.([]byte) + if !ok { + return nil, errors.New("invalid raw, expected byte array") + } + + if len(der) == 0 { + return nil, errors.New("invalid raw, it must not be nil") + } + + key, err := i.Revocation.NewKeyFromBytes(raw.([]byte)) + if err != nil { + return nil, err + } + + return &revocationSecretKey{ + privKey: key, + exportable: i.Exportable, + }, nil +} + +type CriSigner struct { + Revocation types.Revocation +} + +func (s *CriSigner) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) ([]byte, error) { + revocationSecretKey, ok := k.(*revocationSecretKey) + if !ok { + return nil, errors.New("invalid key, expected *revocationSecretKey") + } + criOpts, ok := opts.(*bccsp.IdemixCRISignerOpts) + if !ok { + return nil, errors.New("invalid options, expected *IdemixCRISignerOpts") + } + + return s.Revocation.Sign( + revocationSecretKey.privKey, + criOpts.UnrevokedHandles, + criOpts.Epoch, + criOpts.RevocationAlgorithm, + ) +} + +type CriVerifier struct { + Revocation types.Revocation +} + +func (v *CriVerifier) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (bool, error) { + revocationPublicKey, ok := k.(*revocationPublicKey) + if !ok { + return false, errors.New("invalid key, expected *revocationPublicKey") + } + criOpts, ok := opts.(*bccsp.IdemixCRISignerOpts) + if !ok { + return false, errors.New("invalid options, expected *IdemixCRISignerOpts") + } + if len(signature) == 0 { + return false, errors.New("invalid signature, it must not be empty") + } + + err := v.Revocation.Verify( + revocationPublicKey.pubKey, + signature, + criOpts.Epoch, + criOpts.RevocationAlgorithm, + ) + if err != nil { + return false, err + } + + return true, nil +} diff --git a/vendor/github.com/IBM/idemix/bccsp/handlers/signer.go b/vendor/github.com/IBM/idemix/bccsp/handlers/signer.go new file mode 100644 index 000000000..bd0e76b69 --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/handlers/signer.go @@ -0,0 +1,175 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package handlers + +import ( + "crypto/ecdsa" + "github.com/pkg/errors" + + "github.com/IBM/idemix/bccsp/types" + bccsp "github.com/IBM/idemix/bccsp/types" +) + +type Signer struct { + SignatureScheme types.SignatureScheme +} + +func (s *Signer) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) ([]byte, error) { + userSecretKey, ok := k.(*UserSecretKey) + if !ok { + return nil, errors.New("invalid key, expected *userSecretKey") + } + + signerOpts, ok := opts.(*bccsp.IdemixSignerOpts) + if !ok { + return nil, errors.New("invalid options, expected *IdemixSignerOpts") + } + + // Issuer public key + if signerOpts.IssuerPK == nil { + return nil, errors.New("invalid options, missing issuer public key") + } + ipk, ok := signerOpts.IssuerPK.(*issuerPublicKey) + if !ok { + return nil, errors.New("invalid issuer public key, expected *issuerPublicKey") + } + + // Nym + if signerOpts.Nym == nil { + return nil, errors.New("invalid options, missing nym key") + } + nymSk, ok := signerOpts.Nym.(*NymSecretKey) + if !ok { + return nil, errors.New("invalid nym key, expected *nymSecretKey") + } + + sigma, meta, err := s.SignatureScheme.Sign( + signerOpts.Credential, + userSecretKey.Sk, + nymSk.Pk, + nymSk.Sk, + ipk.pk, + signerOpts.Attributes, + digest, + signerOpts.RhIndex, + signerOpts.EidIndex, + signerOpts.CRI, + signerOpts.SigType, + signerOpts.Metadata, + ) + if err != nil { + return nil, err + } + + signerOpts.Metadata = meta + + return sigma, nil +} + +type Verifier struct { + SignatureScheme types.SignatureScheme +} + +func (v *Verifier) AuditNymEid(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (bool, error) { + issuerPublicKey, ok := k.(*issuerPublicKey) + if !ok { + return false, errors.New("invalid key, expected *issuerPublicKey") + } + + signerOpts, ok := opts.(*bccsp.EidNymAuditOpts) + if !ok { + return false, errors.New("invalid options, expected *EidNymAuditOpts") + } + + if len(signature) == 0 { + return false, errors.New("invalid signature, it must not be empty") + } + + err := v.SignatureScheme.AuditNymEid( + issuerPublicKey.pk, + signerOpts.EidIndex, + signature, + signerOpts.EnrollmentID, + signerOpts.RNymEid, + signerOpts.AuditVerificationType, + ) + if err != nil { + return false, err + } + + return true, nil +} + +func (v *Verifier) AuditNymRh(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (bool, error) { + issuerPublicKey, ok := k.(*issuerPublicKey) + if !ok { + return false, errors.New("invalid key, expected *issuerPublicKey") + } + + signerOpts, ok := opts.(*bccsp.RhNymAuditOpts) + if !ok { + return false, errors.New("invalid options, expected *RhNymAuditOpts") + } + + if len(signature) == 0 { + return false, errors.New("invalid signature, it must not be empty") + } + + err := v.SignatureScheme.AuditNymRh( + issuerPublicKey.pk, + signerOpts.RhIndex, + signature, + signerOpts.RevocationHandle, + signerOpts.RNymRh, + signerOpts.AuditVerificationType, + ) + if err != nil { + return false, err + } + + return true, nil +} + +func (v *Verifier) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (bool, error) { + issuerPublicKey, ok := k.(*issuerPublicKey) + if !ok { + return false, errors.New("invalid key, expected *issuerPublicKey") + } + + signerOpts, ok := opts.(*bccsp.IdemixSignerOpts) + if !ok { + return false, errors.New("invalid options, expected *IdemixSignerOpts") + } + + var rPK *ecdsa.PublicKey + if signerOpts.RevocationPublicKey != nil { + revocationPK, ok := signerOpts.RevocationPublicKey.(*revocationPublicKey) + if !ok { + return false, errors.New("invalid options, expected *revocationPublicKey") + } + rPK = revocationPK.pubKey + } + + if len(signature) == 0 { + return false, errors.New("invalid signature, it must not be empty") + } + err := v.SignatureScheme.Verify( + issuerPublicKey.pk, + signature, + digest, + signerOpts.Attributes, + signerOpts.RhIndex, + signerOpts.EidIndex, + rPK, + signerOpts.Epoch, + signerOpts.VerificationType, + signerOpts.Metadata, + ) + if err != nil { + return false, err + } + return true, nil +} diff --git a/vendor/github.com/IBM/idemix/bccsp/handlers/user.go b/vendor/github.com/IBM/idemix/bccsp/handlers/user.go new file mode 100644 index 000000000..34295f779 --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/handlers/user.go @@ -0,0 +1,98 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package handlers + +import ( + "crypto/sha256" + + "github.com/IBM/idemix/bccsp/types" + bccsp "github.com/IBM/idemix/bccsp/types" + math "github.com/IBM/mathlib" + "github.com/pkg/errors" +) + +// UserSecretKey contains the User secret key +type UserSecretKey struct { + // Sk is the idemix reference to the User key + Sk *math.Zr + // Exportable if true, sk can be exported via the Bytes function + Exportable bool +} + +func NewUserSecretKey(sk *math.Zr, exportable bool) *UserSecretKey { + return &UserSecretKey{Sk: sk, Exportable: exportable} +} + +func (k *UserSecretKey) Bytes() ([]byte, error) { + if k.Exportable { + return k.Sk.Bytes(), nil + } + + return nil, errors.New("not exportable") +} + +func (k *UserSecretKey) SKI() []byte { + raw := k.Sk.Bytes() + hash := sha256.New() + hash.Write(raw) + return hash.Sum(nil) +} + +func (*UserSecretKey) Symmetric() bool { + return true +} + +func (*UserSecretKey) Private() bool { + return true +} + +func (k *UserSecretKey) PublicKey() (bccsp.Key, error) { + return nil, errors.New("cannot call this method on a symmetric key") +} + +type UserKeyGen struct { + // Exportable is a flag to allow an issuer secret key to be marked as Exportable. + // If a secret key is marked as Exportable, its Bytes method will return the key's byte representation. + Exportable bool + // User implements the underlying cryptographic algorithms + User types.User +} + +func (g *UserKeyGen) KeyGen(opts bccsp.KeyGenOpts) (bccsp.Key, error) { + sk, err := g.User.NewKey() + if err != nil { + return nil, err + } + + return &UserSecretKey{Exportable: g.Exportable, Sk: sk}, nil +} + +// UserKeyImporter import user keys +type UserKeyImporter struct { + // Exportable is a flag to allow a secret key to be marked as Exportable. + // If a secret key is marked as Exportable, its Bytes method will return the key's byte representation. + Exportable bool + // User implements the underlying cryptographic algorithms + User types.User +} + +func (i *UserKeyImporter) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.Key, err error) { + der, ok := raw.([]byte) + if !ok { + return nil, errors.New("invalid raw, expected byte array") + } + + if len(der) == 0 { + return nil, errors.New("invalid raw, it must not be nil") + } + + sk, err := i.User.NewKeyFromBytes(raw.([]byte)) + if err != nil { + return nil, err + } + + return &UserSecretKey{Exportable: i.Exportable, Sk: sk}, nil +} diff --git a/vendor/github.com/IBM/idemix/bccsp/impl.go b/vendor/github.com/IBM/idemix/bccsp/impl.go new file mode 100644 index 000000000..7b92c189d --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/impl.go @@ -0,0 +1,410 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package idemix + +import ( + "hash" + "reflect" + + bccsp "github.com/IBM/idemix/bccsp/types" + "github.com/IBM/idemix/common/flogging" + "github.com/pkg/errors" +) + +var ( + logger = flogging.MustGetLogger("bccsp_idemix") +) + +// KeyGenerator is a BCCSP-like interface that provides key generation algorithms +type KeyGenerator interface { + + // KeyGen generates a key using opts. + KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) +} + +// KeyDeriver is a BCCSP-like interface that provides key derivation algorithms +type KeyDeriver interface { + + // KeyDeriv derives a key from k using opts. + // The opts argument should be appropriate for the primitive used. + KeyDeriv(k bccsp.Key, opts bccsp.KeyDerivOpts) (dk bccsp.Key, err error) +} + +// KeyImporter is a BCCSP-like interface that provides key import algorithms +type KeyImporter interface { + + // KeyImport imports a key from its raw representation using opts. + // The opts argument should be appropriate for the primitive used. + KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.Key, err error) +} + +// Encryptor is a BCCSP-like interface that provides encryption algorithms +type Encryptor interface { + + // Encrypt encrypts plaintext using key k. + // The opts argument should be appropriate for the algorithm used. + Encrypt(k bccsp.Key, plaintext []byte, opts bccsp.EncrypterOpts) (ciphertext []byte, err error) +} + +// Decryptor is a BCCSP-like interface that provides decryption algorithms +type Decryptor interface { + + // Decrypt decrypts ciphertext using key k. + // The opts argument should be appropriate for the algorithm used. + Decrypt(k bccsp.Key, ciphertext []byte, opts bccsp.DecrypterOpts) (plaintext []byte, err error) +} + +// Signer is a BCCSP-like interface that provides signing algorithms +type Signer interface { + + // Sign signs digest using key k. + // The opts argument should be appropriate for the algorithm used. + // + // Note that when a signature of a hash of a larger message is needed, + // the caller is responsible for hashing the larger message and passing + // the hash (as digest). + Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error) +} + +// Verifier is a BCCSP-like interface that provides verifying algorithms +type Verifier interface { + + // Verify verifies signature against key k and digest + // The opts argument should be appropriate for the algorithm used. + Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) +} + +// Hasher is a BCCSP-like interface that provides hash algorithms +type Hasher interface { + + // Hash hashes messages msg using options opts. + // If opts is nil, the default hash function will be used. + Hash(msg []byte, opts bccsp.HashOpts) (hash []byte, err error) + + // GetHash returns and instance of hash.Hash using options opts. + // If opts is nil, the default hash function will be returned. + GetHash(opts bccsp.HashOpts) (h hash.Hash, err error) +} + +// CSP provides a generic implementation of the BCCSP interface based +// on wrappers. It can be customized by providing implementations for the +// following algorithm-based wrappers: KeyGenerator, KeyDeriver, KeyImporter, +// Encryptor, Decryptor, Signer, Verifier, Hasher. Each wrapper is bound to a +// goland type representing either an option or a key. +type CSP struct { + ks bccsp.KeyStore + + KeyGenerators map[reflect.Type]KeyGenerator + KeyDerivers map[reflect.Type]KeyDeriver + KeyImporters map[reflect.Type]KeyImporter + Encryptors map[reflect.Type]Encryptor + Decryptors map[reflect.Type]Decryptor + Signers map[reflect.Type]Signer + Verifiers map[reflect.Type]Verifier + Hashers map[reflect.Type]Hasher +} + +func NewImpl(keyStore bccsp.KeyStore) (*CSP, error) { + if keyStore == nil { + return nil, errors.Errorf("Invalid bccsp.KeyStore instance. It must be different from nil.") + } + + encryptors := make(map[reflect.Type]Encryptor) + decryptors := make(map[reflect.Type]Decryptor) + signers := make(map[reflect.Type]Signer) + verifiers := make(map[reflect.Type]Verifier) + hashers := make(map[reflect.Type]Hasher) + keyGenerators := make(map[reflect.Type]KeyGenerator) + keyDerivers := make(map[reflect.Type]KeyDeriver) + keyImporters := make(map[reflect.Type]KeyImporter) + + csp := &CSP{keyStore, + keyGenerators, keyDerivers, keyImporters, encryptors, + decryptors, signers, verifiers, hashers} + + return csp, nil +} + +// KeyGen generates a key using opts. +func (csp *CSP) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) { + // Validate arguments + if opts == nil { + return nil, errors.New("Invalid Opts parameter. It must not be nil.") + } + + keyGenerator, found := csp.KeyGenerators[reflect.TypeOf(opts)] + if !found { + return nil, errors.Errorf("Unsupported 'KeyGenOpts' provided [%v]", opts) + } + + k, err = keyGenerator.KeyGen(opts) + if err != nil { + return nil, errors.Wrapf(err, "Failed generating key with opts [%v]", opts) + } + + // If the key is not Ephemeral, store it. + if !opts.Ephemeral() { + // Store the key + err = csp.ks.StoreKey(k) + if err != nil { + return nil, errors.Wrapf(err, "Failed storing key [%s]", opts.Algorithm()) + } + } + + return k, nil +} + +// KeyDeriv derives a key from k using opts. +// The opts argument should be appropriate for the primitive used. +func (csp *CSP) KeyDeriv(k bccsp.Key, opts bccsp.KeyDerivOpts) (dk bccsp.Key, err error) { + // Validate arguments + if k == nil { + return nil, errors.New("Invalid Key. It must not be nil.") + } + if opts == nil { + return nil, errors.New("Invalid opts. It must not be nil.") + } + + keyDeriver, found := csp.KeyDerivers[reflect.TypeOf(k)] + if !found { + return nil, errors.Errorf("Unsupported 'Key' provided [%v]", k) + } + + k, err = keyDeriver.KeyDeriv(k, opts) + if err != nil { + return nil, errors.Wrapf(err, "Failed deriving key with opts [%v]", opts) + } + + // If the key is not Ephemeral, store it. + if !opts.Ephemeral() { + // Store the key + err = csp.ks.StoreKey(k) + if err != nil { + return nil, errors.Wrapf(err, "Failed storing key [%s]", opts.Algorithm()) + } + } + + return k, nil +} + +// KeyImport imports a key from its raw representation using opts. +// The opts argument should be appropriate for the primitive used. +func (csp *CSP) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.Key, err error) { + // Validate arguments + if raw == nil { + return nil, errors.New("Invalid raw. It must not be nil.") + } + if opts == nil { + return nil, errors.New("Invalid opts. It must not be nil.") + } + + keyImporter, found := csp.KeyImporters[reflect.TypeOf(opts)] + if !found { + return nil, errors.Errorf("Unsupported 'KeyImportOpts' provided [%v]", opts) + } + + k, err = keyImporter.KeyImport(raw, opts) + if err != nil { + return nil, errors.Wrapf(err, "Failed importing key with opts [%v]", opts) + } + + // If the key is not Ephemeral, store it. + if !opts.Ephemeral() { + // Store the key + err = csp.ks.StoreKey(k) + if err != nil { + return nil, errors.Wrapf(err, "Failed storing imported key with opts [%v]", opts) + } + } + + return +} + +// GetKey returns the key this CSP associates to +// the Subject Key Identifier ski. +func (csp *CSP) GetKey(ski []byte) (k bccsp.Key, err error) { + k, err = csp.ks.GetKey(ski) + if err != nil { + return nil, errors.Wrapf(err, "Failed getting key for SKI [%v]", ski) + } + + return +} + +// Hash hashes messages msg using options opts. +func (csp *CSP) Hash(msg []byte, opts bccsp.HashOpts) (digest []byte, err error) { + // Validate arguments + if opts == nil { + return nil, errors.New("Invalid opts. It must not be nil.") + } + + hasher, found := csp.Hashers[reflect.TypeOf(opts)] + if !found { + return nil, errors.Errorf("Unsupported 'HashOpt' provided [%v]", opts) + } + + digest, err = hasher.Hash(msg, opts) + if err != nil { + return nil, errors.Wrapf(err, "Failed hashing with opts [%v]", opts) + } + + return +} + +// GetHash returns and instance of hash.Hash using options opts. +// If opts is nil then the default hash function is returned. +func (csp *CSP) GetHash(opts bccsp.HashOpts) (h hash.Hash, err error) { + // Validate arguments + if opts == nil { + return nil, errors.New("Invalid opts. It must not be nil.") + } + + hasher, found := csp.Hashers[reflect.TypeOf(opts)] + if !found { + return nil, errors.Errorf("Unsupported 'HashOpt' provided [%v]", opts) + } + + h, err = hasher.GetHash(opts) + if err != nil { + return nil, errors.Wrapf(err, "Failed getting hash function with opts [%v]", opts) + } + + return +} + +// Sign signs digest using key k. +// The opts argument should be appropriate for the primitive used. +// +// Note that when a signature of a hash of a larger message is needed, +// the caller is responsible for hashing the larger message and passing +// the hash (as digest). +func (csp *CSP) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error) { + // Validate arguments + if k == nil { + return nil, errors.New("Invalid Key. It must not be nil.") + } + if len(digest) == 0 { + return nil, errors.New("Invalid digest. Cannot be empty.") + } + + keyType := reflect.TypeOf(k) + signer, found := csp.Signers[keyType] + if !found { + return nil, errors.Errorf("Unsupported 'SignKey' provided [%s]", keyType) + } + + signature, err = signer.Sign(k, digest, opts) + if err != nil { + return nil, errors.Wrapf(err, "Failed signing with opts [%v]", opts) + } + + return +} + +// Verify verifies signature against key k and digest +func (csp *CSP) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) { + // Validate arguments + if k == nil { + return false, errors.New("Invalid Key. It must not be nil.") + } + if len(signature) == 0 { + return false, errors.New("Invalid signature. Cannot be empty.") + } + if len(digest) == 0 { + return false, errors.New("Invalid digest. Cannot be empty.") + } + + verifier, found := csp.Verifiers[reflect.TypeOf(k)] + if !found { + return false, errors.Errorf("Unsupported 'VerifyKey' provided [%v]", k) + } + + valid, err = verifier.Verify(k, signature, digest, opts) + if err != nil { + return false, errors.Wrapf(err, "Failed verifing with opts [%v]", opts) + } + + return +} + +// Encrypt encrypts plaintext using key k. +// The opts argument should be appropriate for the primitive used. +func (csp *CSP) Encrypt(k bccsp.Key, plaintext []byte, opts bccsp.EncrypterOpts) ([]byte, error) { + // Validate arguments + if k == nil { + return nil, errors.New("Invalid Key. It must not be nil.") + } + + encryptor, found := csp.Encryptors[reflect.TypeOf(k)] + if !found { + return nil, errors.Errorf("Unsupported 'EncryptKey' provided [%v]", k) + } + + return encryptor.Encrypt(k, plaintext, opts) +} + +// Decrypt decrypts ciphertext using key k. +// The opts argument should be appropriate for the primitive used. +func (csp *CSP) Decrypt(k bccsp.Key, ciphertext []byte, opts bccsp.DecrypterOpts) (plaintext []byte, err error) { + // Validate arguments + if k == nil { + return nil, errors.New("Invalid Key. It must not be nil.") + } + + decryptor, found := csp.Decryptors[reflect.TypeOf(k)] + if !found { + return nil, errors.Errorf("Unsupported 'DecryptKey' provided [%v]", k) + } + + plaintext, err = decryptor.Decrypt(k, ciphertext, opts) + if err != nil { + return nil, errors.Wrapf(err, "Failed decrypting with opts [%v]", opts) + } + + return +} + +// AddWrapper binds the passed type to the passed wrapper. +// Notice that that wrapper must be an instance of one of the following interfaces: +// KeyGenerator, KeyDeriver, KeyImporter, Encryptor, Decryptor, Signer, Verifier, Hasher. +func (csp *CSP) AddWrapper(t reflect.Type, w interface{}) error { + if t == nil { + return errors.Errorf("type cannot be nil") + } + if w == nil { + return errors.Errorf("wrapper cannot be nil") + } + switch dt := w.(type) { + case KeyGenerator: + csp.KeyGenerators[t] = dt + case KeyImporter: + csp.KeyImporters[t] = dt + case KeyDeriver: + csp.KeyDerivers[t] = dt + case Encryptor: + csp.Encryptors[t] = dt + case Decryptor: + csp.Decryptors[t] = dt + case Signer: + csp.Signers[t] = dt + case Verifier: + csp.Verifiers[t] = dt + case Hasher: + csp.Hashers[t] = dt + default: + return errors.Errorf("wrapper type not valid, must be on of: KeyGenerator, KeyDeriver, KeyImporter, Encryptor, Decryptor, Signer, Verifier, Hasher") + } + return nil +} diff --git a/vendor/github.com/IBM/idemix/bccsp/keystore/dummy.go b/vendor/github.com/IBM/idemix/bccsp/keystore/dummy.go new file mode 100644 index 000000000..c00003833 --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/keystore/dummy.go @@ -0,0 +1,34 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package keystore + +import ( + "errors" + + bccsp "github.com/IBM/idemix/bccsp/types" +) + +// Dummy is a read-only KeyStore that neither loads nor stores keys. +type Dummy struct { +} + +// ReadOnly returns true if this KeyStore is read only, false otherwise. +// If ReadOnly is true then StoreKey will fail. +func (ks *Dummy) ReadOnly() bool { + return true +} + +// GetKey returns a key object whose SKI is the one passed. +func (ks *Dummy) GetKey(ski []byte) (bccsp.Key, error) { + return nil, errors.New("key not found. This is a dummy KeyStore") +} + +// StoreKey stores the key k in this KeyStore. +// If this KeyStore is read only then the method will fail. +func (ks *Dummy) StoreKey(k bccsp.Key) error { + return errors.New("cannot store key. This is a dummy read-only KeyStore") +} diff --git a/vendor/github.com/IBM/idemix/bccsp/keystore/kvsbased.go b/vendor/github.com/IBM/idemix/bccsp/keystore/kvsbased.go new file mode 100644 index 000000000..f15eff41a --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/keystore/kvsbased.go @@ -0,0 +1,121 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package keystore + +import ( + "encoding/hex" + + "github.com/IBM/idemix/bccsp/handlers" + idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" + "github.com/IBM/idemix/bccsp/schemes/dlog/crypto/translator/amcl" + bccsp "github.com/IBM/idemix/bccsp/types" + math "github.com/IBM/mathlib" + "github.com/pkg/errors" +) + +type KVS interface { + Put(id string, state interface{}) error + Get(id string, state interface{}) error +} + +type NymSecretKey struct { + Ski []byte + Sk []byte + Pk *amcl.ECP + Exportable bool +} + +type UserSecretKey struct { + Sk []byte + Exportable bool +} + +type entry struct { + NymSecretKey *NymSecretKey `json:",omitempty"` + UserSecretKey *UserSecretKey `json:",omitempty"` +} + +// KVSStore is a read-only KeyStore that neither loads nor stores keys. +type KVSStore struct { + KVS + Translator idemix.Translator + Curve *math.Curve +} + +// ReadOnly returns true if this KeyStore is read only, false otherwise. +// If ReadOnly is true then StoreKey will fail. +func (ks *KVSStore) ReadOnly() bool { + return false +} + +// GetKey returns a key object whose SKI is the one passed. +func (ks *KVSStore) GetKey(ski []byte) (bccsp.Key, error) { + id := hex.EncodeToString(ski) + + entry := &entry{} + err := ks.KVS.Get(id, entry) + if err != nil { + return nil, errors.Wrapf(err, "could not get key [%s] from kvs", id) + } + + switch { + case entry.NymSecretKey != nil: + pk, err := ks.Translator.G1FromProto(entry.NymSecretKey.Pk) + if err != nil { + return nil, err + } + + return &handlers.NymSecretKey{ + Exportable: entry.NymSecretKey.Exportable, + Sk: ks.Curve.NewZrFromBytes(entry.NymSecretKey.Sk), + Ski: entry.NymSecretKey.Ski, + Pk: pk, + Translator: ks.Translator, + }, nil + case entry.UserSecretKey != nil: + return &handlers.UserSecretKey{ + Exportable: entry.UserSecretKey.Exportable, + Sk: ks.Curve.NewZrFromBytes(entry.UserSecretKey.Sk), + }, nil + default: + return nil, errors.Errorf("key not found for [%s]", id) + } +} + +// StoreKey stores the key k in this KeyStore. +// If this KeyStore is read only then the method will fail. +func (ks *KVSStore) StoreKey(k bccsp.Key) error { + entry := &entry{} + var id string + + switch key := k.(type) { + case *handlers.NymSecretKey: + entry.NymSecretKey = &NymSecretKey{ + Ski: key.Ski, + Sk: key.Sk.Bytes(), + Pk: ks.Translator.G1ToProto(key.Pk), + Exportable: key.Exportable, + } + + pk, err := k.PublicKey() + if err != nil { + return errors.Errorf("could not get public version for key [%s]", k.SKI()) + } + + id = hex.EncodeToString(pk.SKI()) + case *handlers.UserSecretKey: + entry.UserSecretKey = &UserSecretKey{ + Sk: key.Sk.Bytes(), + Exportable: key.Exportable, + } + id = hex.EncodeToString(k.SKI()) + default: + return errors.Errorf("unknown type [%T] for the supplied key", key) + } + + return ks.KVS.Put(id, entry) +} diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/aries/LICENSE b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/aries/blind_sign.go b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/blind_sign.go new file mode 100644 index 000000000..3478e76b4 --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/blind_sign.go @@ -0,0 +1,202 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package aries + +import ( + "crypto/rand" + "errors" + "fmt" + + ml "github.com/IBM/mathlib" + "github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub" +) + +// BlindedMessages represents a set of messages prepared +// (blinded) to be submitted to a signer for a blind signature. +type BlindedMessages struct { + PK *bbs12381g2pub.PublicKeyWithGenerators + S *ml.Zr + C *ml.G1 + PoK *POKOfBlindedMessages +} + +func (b *BlindedMessages) Bytes() []byte { + bytes := make([]byte, 0) + + bytes = append(bytes, b.C.Compressed()...) + bytes = append(bytes, b.PoK.C.Compressed()...) + bytes = append(bytes, b.PoK.ProofC.ToBytes()...) + + return bytes +} + +func ParseBlindedMessages(bytes []byte, curve *ml.Curve) (*BlindedMessages, error) { + offset := 0 + + C, err := curve.NewG1FromCompressed(bytes[offset : offset+curve.CompressedG1ByteSize]) + if err != nil { + return nil, fmt.Errorf("parse G1 point (C): %w", err) + } + + offset += curve.CompressedG1ByteSize + + PoKC, err := curve.NewG1FromCompressed(bytes[offset : offset+curve.CompressedG1ByteSize]) + if err != nil { + return nil, fmt.Errorf("parse G1 point (PoKC): %w", err) + } + + offset += curve.CompressedG1ByteSize + + proof, err := bbs12381g2pub.ParseProofG1(bytes[offset:]) + if err != nil { + return nil, fmt.Errorf("parse G1 proof: %w", err) + } + + return &BlindedMessages{ + C: C, + PoK: &POKOfBlindedMessages{ + C: PoKC, + ProofC: proof, + }, + }, nil +} + +// POKOfBlindedMessages is the zero-knowledge proof that the +// requester knows the messages they have submitted for blind +// signature in the form of a Pedersen commitment. +type POKOfBlindedMessages struct { + C *ml.G1 + ProofC *bbs12381g2pub.ProofG1 +} + +// VerifyProof verifies the correctness of the zero knowledge +// proof against the supplied commitment, challenge and public key. +func (b *POKOfBlindedMessages) VerifyProof(messages []bool, commitment *ml.G1, challenge *ml.Zr, PK *bbs12381g2pub.PublicKey) error { + pubKeyWithGenerators, err := PK.ToPublicKeyWithGenerators(len(messages)) + if err != nil { + return fmt.Errorf("build generators from public key: %w", err) + } + + bases := []*ml.G1{pubKeyWithGenerators.H0} + + for i, in := range messages { + if !in { + continue + } + + bases = append(bases, pubKeyWithGenerators.H[i]) + } + + err = b.ProofC.Verify(bases, commitment, challenge) + if err != nil { + return errors.New("invalid proof") + } + + return nil +} + +// VerifyBlinding verifies that `msgCommit` is a valid +// commitment of a set of messages against the appropriate bases. +func VerifyBlinding(messageBitmap []bool, msgCommit *ml.G1, bmProof *POKOfBlindedMessages, PK *bbs12381g2pub.PublicKey, nonce []byte) error { + challengeBytes := msgCommit.Bytes() + challengeBytes = append(challengeBytes, bmProof.C.Bytes()...) + challengeBytes = append(challengeBytes, nonce...) + + return bmProof.VerifyProof(messageBitmap, msgCommit, bbs12381g2pub.FrFromOKM(challengeBytes), PK) +} + +// BlindMessages constructs a commitment to a set of messages +// that need to be blinded before signing, and generates the +// corresponding ZKP. +func BlindMessages(messages [][]byte, PK *bbs12381g2pub.PublicKey, blindedMsgCount int, nonce []byte, curve *ml.Curve) (*BlindedMessages, error) { + zrs := make([]*ml.Zr, len(messages)) + + for i, msg := range messages { + if len(msg) == 0 { + continue + } + + zrs[i] = bbs12381g2pub.FrFromOKM(msg) + } + + return BlindMessagesZr(zrs, PK, blindedMsgCount, nonce, curve) +} + +// BlindMessagesZr constructs a commitment to a set of messages +// that need to be blinded before signing, and generates the +// corresponding ZKP. +func BlindMessagesZr(zrs []*ml.Zr, PK *bbs12381g2pub.PublicKey, blindedMsgCount int, nonce []byte, curve *ml.Curve) (*BlindedMessages, error) { + pubKeyWithGenerators, err := PK.ToPublicKeyWithGenerators(len(zrs)) + if err != nil { + return nil, fmt.Errorf("build generators from public key: %w", err) + } + + commit := bbs12381g2pub.NewProverCommittingG1() + cb := bbs12381g2pub.NewCommitmentBuilder(blindedMsgCount + 1) + secrets := make([]*ml.Zr, 0, blindedMsgCount+1) + + s := curve.NewRandomZr(rand.Reader) + + commit.Commit(pubKeyWithGenerators.H0) + cb.Add(pubKeyWithGenerators.H0, s) + secrets = append(secrets, s) + + for i, zr := range zrs { + if zr == nil { + continue + } + + commit.Commit(pubKeyWithGenerators.H[i]) + cb.Add(pubKeyWithGenerators.H[i], zr) + secrets = append(secrets, zr) + } + + C := cb.Build() + U := commit.Finish() + + challengeBytes := C.Bytes() + challengeBytes = append(challengeBytes, U.Commitment.Bytes()...) + challengeBytes = append(challengeBytes, nonce...) + + return &BlindedMessages{ + PK: pubKeyWithGenerators, + S: s, + C: C, + PoK: &POKOfBlindedMessages{ + C: U.Commitment, + ProofC: U.GenerateProof(bbs12381g2pub.FrFromOKM(challengeBytes), secrets), + }, + }, nil +} + +// BlindSign signs disclosed and blinded messages using private key in compressed form. +func BlindSign(messages []*bbs12381g2pub.SignatureMessage, msgCount int, commitment *ml.G1, privKeyBytes []byte) ([]byte, error) { + privKey, err := bbs12381g2pub.UnmarshalPrivateKey(privKeyBytes) + if err != nil { + return nil, fmt.Errorf("unmarshal private key: %w", err) + } + + if len(messages) == 0 { + return nil, errors.New("messages are not defined") + } + + bbs := bbs12381g2pub.New() + + return bbs.SignWithKeyFr(messages, msgCount, commitment, privKey) +} + +// UnblindSign converts a signature over some blind messages into a standard signature. +func UnblindSign(sigBytes []byte, S *ml.Zr, curve *ml.Curve) ([]byte, error) { + signature, err := bbs12381g2pub.ParseSignature(sigBytes) + if err != nil { + return nil, fmt.Errorf("parse signature: %w", err) + } + + signature.S = curve.ModAdd(signature.S, S, curve.GroupOrder) + + return signature.ToBytes() +} diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/aries/cred.go b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/cred.go new file mode 100644 index 000000000..a2659a74f --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/cred.go @@ -0,0 +1,109 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package aries + +import ( + "fmt" + + "github.com/IBM/idemix/bccsp/types" + math "github.com/IBM/mathlib" + "github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub" + "github.com/golang/protobuf/proto" + "github.com/pkg/errors" +) + +type Cred struct { + Bls *bbs12381g2pub.BBSG2Pub + Curve *math.Curve +} + +// Sign issues a new credential, which is the last step of the interactive issuance protocol +// All attribute values are added by the issuer at this step and then signed together with a commitment to +// the user's secret key from a credential request +func (c *Cred) Sign(key types.IssuerSecretKey, credentialRequest []byte, attributes []types.IdemixAttribute) ([]byte, error) { + isk, ok := key.(*IssuerSecretKey) + if !ok { + return nil, errors.Errorf("invalid issuer public key, expected *IssuerPublicKey, got [%T]", key) + } + + blindedMsg, err := ParseBlindedMessages(credentialRequest, c.Curve) + if err != nil { + return nil, fmt.Errorf("ParseBlindedMessages failed [%w]", err) + } + + msgsZr := attributesToSignatureMessage(nil, attributes, c.Curve) + + sig, err := BlindSign(msgsZr, len(attributes)+1, blindedMsg.C, isk.SK.FR.Bytes()) + if err != nil { + return nil, fmt.Errorf("ParseBlindedMessages failed [%w]", err) + } + + attrs := make([][]byte, len(attributes)) + for i, msg := range msgsZr { + attrs[i] = msg.FR.Bytes() + } + + cred := &Credential{ + Cred: sig, + Attrs: attrs, + } + + credBytes, err := proto.Marshal(cred) + if err != nil { + return nil, fmt.Errorf("proto.Marshal failed [%w]", err) + } + + return credBytes, nil +} + +// Verify cryptographically verifies the credential by verifying the signature +// on the attribute values and user's secret key +func (c *Cred) Verify(sk *math.Zr, key types.IssuerPublicKey, credBytes []byte, attributes []types.IdemixAttribute) error { + ipk, ok := key.(*IssuerPublicKey) + if !ok { + return errors.Errorf("invalid issuer public key, expected *IssuerPublicKey, got [%T]", ipk) + } + + credential := &Credential{} + err := proto.Unmarshal(credBytes, credential) + if err != nil { + return fmt.Errorf("proto.Unmarshal failed [%w]", err) + } + + sigma, err := bbs12381g2pub.ParseSignature(credential.Cred) + if err != nil { + return fmt.Errorf("ParseSignature failed [%w]", err) + } + + sm := make([]*bbs12381g2pub.SignatureMessage, len(credential.Attrs)+1) + sm[0] = &bbs12381g2pub.SignatureMessage{ + FR: sk, + Idx: 0, + } + for i, v := range credential.Attrs { + sm[i+1] = &bbs12381g2pub.SignatureMessage{ + FR: c.Curve.NewZrFromBytes(v), + Idx: i + 1, + } + + switch attributes[i].Type { + case types.IdemixHiddenAttribute: + continue + case types.IdemixBytesAttribute: + fr := bbs12381g2pub.FrFromOKM(attributes[i].Value.([]byte)) + if !fr.Equals(sm[i+1].FR) { + return errors.Errorf("credential does not contain the correct attribute value at position [%d]", i) + } + case types.IdemixIntAttribute: + fr := c.Curve.NewZrFromInt(int64(attributes[i].Value.(int))) + if !fr.Equals(sm[i+1].FR) { + return errors.Errorf("credential does not contain the correct attribute value at position [%d]", i) + } + } + } + + return sigma.Verify(sm, ipk.PKwG) +} diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/aries/cred.pb.go b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/cred.pb.go new file mode 100644 index 000000000..c56551e2c --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/cred.pb.go @@ -0,0 +1,433 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: bccsp/schemes/aries/cred.proto + +package aries + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +// Credential specifies a credential object +type Credential struct { + Cred []byte `protobuf:"bytes,1,opt,name=cred,proto3" json:"cred,omitempty"` + Attrs [][]byte `protobuf:"bytes,2,rep,name=attrs,proto3" json:"attrs,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Credential) Reset() { *m = Credential{} } +func (m *Credential) String() string { return proto.CompactTextString(m) } +func (*Credential) ProtoMessage() {} +func (*Credential) Descriptor() ([]byte, []int) { + return fileDescriptor_57701eac61520cf0, []int{0} +} + +func (m *Credential) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Credential.Unmarshal(m, b) +} +func (m *Credential) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Credential.Marshal(b, m, deterministic) +} +func (m *Credential) XXX_Merge(src proto.Message) { + xxx_messageInfo_Credential.Merge(m, src) +} +func (m *Credential) XXX_Size() int { + return xxx_messageInfo_Credential.Size(m) +} +func (m *Credential) XXX_DiscardUnknown() { + xxx_messageInfo_Credential.DiscardUnknown(m) +} + +var xxx_messageInfo_Credential proto.InternalMessageInfo + +func (m *Credential) GetCred() []byte { + if m != nil { + return m.Cred + } + return nil +} + +func (m *Credential) GetAttrs() [][]byte { + if m != nil { + return m.Attrs + } + return nil +} + +// Signature is a PoK of a BBS+ signature (a credential) +type Signature struct { + MainSignature []byte `protobuf:"bytes,1,opt,name=main_signature,json=mainSignature,proto3" json:"main_signature,omitempty"` + Nonce []byte `protobuf:"bytes,2,opt,name=nonce,proto3" json:"nonce,omitempty"` + Nym []byte `protobuf:"bytes,3,opt,name=nym,proto3" json:"nym,omitempty"` + NymProof []byte `protobuf:"bytes,4,opt,name=nym_proof,json=nymProof,proto3" json:"nym_proof,omitempty"` + NymEid []byte `protobuf:"bytes,5,opt,name=nym_eid,json=nymEid,proto3" json:"nym_eid,omitempty"` + NymEidProof []byte `protobuf:"bytes,6,opt,name=nym_eid_proof,json=nymEidProof,proto3" json:"nym_eid_proof,omitempty"` + NymEidIdx int32 `protobuf:"varint,7,opt,name=nym_eid_idx,json=nymEidIdx,proto3" json:"nym_eid_idx,omitempty"` + NymRh []byte `protobuf:"bytes,8,opt,name=nym_rh,json=nymRh,proto3" json:"nym_rh,omitempty"` + NymRhProof []byte `protobuf:"bytes,9,opt,name=nym_rh_proof,json=nymRhProof,proto3" json:"nym_rh_proof,omitempty"` + NymRhIdx int32 `protobuf:"varint,10,opt,name=nym_rh_idx,json=nymRhIdx,proto3" json:"nym_rh_idx,omitempty"` + RevocationEpochPk []byte `protobuf:"bytes,11,opt,name=revocation_epoch_pk,json=revocationEpochPk,proto3" json:"revocation_epoch_pk,omitempty"` + RevocationPkSig []byte `protobuf:"bytes,12,opt,name=revocation_pk_sig,json=revocationPkSig,proto3" json:"revocation_pk_sig,omitempty"` + Epoch int64 `protobuf:"varint,13,opt,name=epoch,proto3" json:"epoch,omitempty"` + NonRevocationProof *NonRevocationProof `protobuf:"bytes,14,opt,name=non_revocation_proof,json=nonRevocationProof,proto3" json:"non_revocation_proof,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Signature) Reset() { *m = Signature{} } +func (m *Signature) String() string { return proto.CompactTextString(m) } +func (*Signature) ProtoMessage() {} +func (*Signature) Descriptor() ([]byte, []int) { + return fileDescriptor_57701eac61520cf0, []int{1} +} + +func (m *Signature) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Signature.Unmarshal(m, b) +} +func (m *Signature) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Signature.Marshal(b, m, deterministic) +} +func (m *Signature) XXX_Merge(src proto.Message) { + xxx_messageInfo_Signature.Merge(m, src) +} +func (m *Signature) XXX_Size() int { + return xxx_messageInfo_Signature.Size(m) +} +func (m *Signature) XXX_DiscardUnknown() { + xxx_messageInfo_Signature.DiscardUnknown(m) +} + +var xxx_messageInfo_Signature proto.InternalMessageInfo + +func (m *Signature) GetMainSignature() []byte { + if m != nil { + return m.MainSignature + } + return nil +} + +func (m *Signature) GetNonce() []byte { + if m != nil { + return m.Nonce + } + return nil +} + +func (m *Signature) GetNym() []byte { + if m != nil { + return m.Nym + } + return nil +} + +func (m *Signature) GetNymProof() []byte { + if m != nil { + return m.NymProof + } + return nil +} + +func (m *Signature) GetNymEid() []byte { + if m != nil { + return m.NymEid + } + return nil +} + +func (m *Signature) GetNymEidProof() []byte { + if m != nil { + return m.NymEidProof + } + return nil +} + +func (m *Signature) GetNymEidIdx() int32 { + if m != nil { + return m.NymEidIdx + } + return 0 +} + +func (m *Signature) GetNymRh() []byte { + if m != nil { + return m.NymRh + } + return nil +} + +func (m *Signature) GetNymRhProof() []byte { + if m != nil { + return m.NymRhProof + } + return nil +} + +func (m *Signature) GetNymRhIdx() int32 { + if m != nil { + return m.NymRhIdx + } + return 0 +} + +func (m *Signature) GetRevocationEpochPk() []byte { + if m != nil { + return m.RevocationEpochPk + } + return nil +} + +func (m *Signature) GetRevocationPkSig() []byte { + if m != nil { + return m.RevocationPkSig + } + return nil +} + +func (m *Signature) GetEpoch() int64 { + if m != nil { + return m.Epoch + } + return 0 +} + +func (m *Signature) GetNonRevocationProof() *NonRevocationProof { + if m != nil { + return m.NonRevocationProof + } + return nil +} + +// NonRevocationProof contains proof that the credential is not revoked +type NonRevocationProof struct { + RevocationAlg int32 `protobuf:"varint,1,opt,name=revocation_alg,json=revocationAlg,proto3" json:"revocation_alg,omitempty"` + NonRevocationProof []byte `protobuf:"bytes,2,opt,name=non_revocation_proof,json=nonRevocationProof,proto3" json:"non_revocation_proof,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *NonRevocationProof) Reset() { *m = NonRevocationProof{} } +func (m *NonRevocationProof) String() string { return proto.CompactTextString(m) } +func (*NonRevocationProof) ProtoMessage() {} +func (*NonRevocationProof) Descriptor() ([]byte, []int) { + return fileDescriptor_57701eac61520cf0, []int{2} +} + +func (m *NonRevocationProof) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_NonRevocationProof.Unmarshal(m, b) +} +func (m *NonRevocationProof) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_NonRevocationProof.Marshal(b, m, deterministic) +} +func (m *NonRevocationProof) XXX_Merge(src proto.Message) { + xxx_messageInfo_NonRevocationProof.Merge(m, src) +} +func (m *NonRevocationProof) XXX_Size() int { + return xxx_messageInfo_NonRevocationProof.Size(m) +} +func (m *NonRevocationProof) XXX_DiscardUnknown() { + xxx_messageInfo_NonRevocationProof.DiscardUnknown(m) +} + +var xxx_messageInfo_NonRevocationProof proto.InternalMessageInfo + +func (m *NonRevocationProof) GetRevocationAlg() int32 { + if m != nil { + return m.RevocationAlg + } + return 0 +} + +func (m *NonRevocationProof) GetNonRevocationProof() []byte { + if m != nil { + return m.NonRevocationProof + } + return nil +} + +type CredentialRevocationInformation struct { + // epoch contains the epoch (time window) in which this CRI is valid + Epoch int64 `protobuf:"varint,1,opt,name=epoch,proto3" json:"epoch,omitempty"` + // epoch_pk is the public key that is used by the revocation authority in this epoch + EpochPk []byte `protobuf:"bytes,2,opt,name=epoch_pk,json=epochPk,proto3" json:"epoch_pk,omitempty"` + // epoch_pk_sig is a signature on the EpochPK valid under the revocation authority's long term key + EpochPkSig []byte `protobuf:"bytes,3,opt,name=epoch_pk_sig,json=epochPkSig,proto3" json:"epoch_pk_sig,omitempty"` + // revocation_alg denotes which revocation algorithm is used + RevocationAlg int32 `protobuf:"varint,4,opt,name=revocation_alg,json=revocationAlg,proto3" json:"revocation_alg,omitempty"` + // revocation_data contains data specific to the revocation algorithm used + RevocationData []byte `protobuf:"bytes,5,opt,name=revocation_data,json=revocationData,proto3" json:"revocation_data,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CredentialRevocationInformation) Reset() { *m = CredentialRevocationInformation{} } +func (m *CredentialRevocationInformation) String() string { return proto.CompactTextString(m) } +func (*CredentialRevocationInformation) ProtoMessage() {} +func (*CredentialRevocationInformation) Descriptor() ([]byte, []int) { + return fileDescriptor_57701eac61520cf0, []int{3} +} + +func (m *CredentialRevocationInformation) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CredentialRevocationInformation.Unmarshal(m, b) +} +func (m *CredentialRevocationInformation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CredentialRevocationInformation.Marshal(b, m, deterministic) +} +func (m *CredentialRevocationInformation) XXX_Merge(src proto.Message) { + xxx_messageInfo_CredentialRevocationInformation.Merge(m, src) +} +func (m *CredentialRevocationInformation) XXX_Size() int { + return xxx_messageInfo_CredentialRevocationInformation.Size(m) +} +func (m *CredentialRevocationInformation) XXX_DiscardUnknown() { + xxx_messageInfo_CredentialRevocationInformation.DiscardUnknown(m) +} + +var xxx_messageInfo_CredentialRevocationInformation proto.InternalMessageInfo + +func (m *CredentialRevocationInformation) GetEpoch() int64 { + if m != nil { + return m.Epoch + } + return 0 +} + +func (m *CredentialRevocationInformation) GetEpochPk() []byte { + if m != nil { + return m.EpochPk + } + return nil +} + +func (m *CredentialRevocationInformation) GetEpochPkSig() []byte { + if m != nil { + return m.EpochPkSig + } + return nil +} + +func (m *CredentialRevocationInformation) GetRevocationAlg() int32 { + if m != nil { + return m.RevocationAlg + } + return 0 +} + +func (m *CredentialRevocationInformation) GetRevocationData() []byte { + if m != nil { + return m.RevocationData + } + return nil +} + +type NymSignature struct { + MainSignature []byte `protobuf:"bytes,1,opt,name=main_signature,json=mainSignature,proto3" json:"main_signature,omitempty"` + Nonce []byte `protobuf:"bytes,2,opt,name=nonce,proto3" json:"nonce,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *NymSignature) Reset() { *m = NymSignature{} } +func (m *NymSignature) String() string { return proto.CompactTextString(m) } +func (*NymSignature) ProtoMessage() {} +func (*NymSignature) Descriptor() ([]byte, []int) { + return fileDescriptor_57701eac61520cf0, []int{4} +} + +func (m *NymSignature) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_NymSignature.Unmarshal(m, b) +} +func (m *NymSignature) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_NymSignature.Marshal(b, m, deterministic) +} +func (m *NymSignature) XXX_Merge(src proto.Message) { + xxx_messageInfo_NymSignature.Merge(m, src) +} +func (m *NymSignature) XXX_Size() int { + return xxx_messageInfo_NymSignature.Size(m) +} +func (m *NymSignature) XXX_DiscardUnknown() { + xxx_messageInfo_NymSignature.DiscardUnknown(m) +} + +var xxx_messageInfo_NymSignature proto.InternalMessageInfo + +func (m *NymSignature) GetMainSignature() []byte { + if m != nil { + return m.MainSignature + } + return nil +} + +func (m *NymSignature) GetNonce() []byte { + if m != nil { + return m.Nonce + } + return nil +} + +func init() { + proto.RegisterType((*Credential)(nil), "aries.Credential") + proto.RegisterType((*Signature)(nil), "aries.Signature") + proto.RegisterType((*NonRevocationProof)(nil), "aries.NonRevocationProof") + proto.RegisterType((*CredentialRevocationInformation)(nil), "aries.CredentialRevocationInformation") + proto.RegisterType((*NymSignature)(nil), "aries.NymSignature") +} + +func init() { proto.RegisterFile("bccsp/schemes/aries/cred.proto", fileDescriptor_57701eac61520cf0) } + +var fileDescriptor_57701eac61520cf0 = []byte{ + // 553 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0x41, 0x8b, 0xd3, 0x40, + 0x14, 0xc7, 0x49, 0xd3, 0xb4, 0xcd, 0x6b, 0xba, 0xba, 0x63, 0xc5, 0x2c, 0xca, 0x1a, 0x0a, 0x62, + 0xf5, 0xd0, 0x88, 0x82, 0xf7, 0x56, 0x17, 0x29, 0xcb, 0x2e, 0x21, 0x7b, 0x59, 0x44, 0x08, 0xd3, + 0x64, 0xb6, 0x1d, 0xda, 0x99, 0x09, 0x49, 0x2a, 0xcd, 0xd7, 0xf1, 0xe8, 0xe7, 0xf0, 0xe4, 0xc7, + 0xf0, 0xb8, 0x9f, 0x42, 0x66, 0x26, 0x35, 0x81, 0xd6, 0xdb, 0x9e, 0x32, 0xef, 0xff, 0x7b, 0xf3, + 0xef, 0xbc, 0xf7, 0x66, 0x0a, 0xe7, 0x8b, 0x38, 0xce, 0x53, 0x3f, 0x8f, 0x57, 0x84, 0x91, 0xdc, + 0xc7, 0x19, 0x25, 0xb9, 0x1f, 0x67, 0x24, 0x99, 0xa4, 0x99, 0x28, 0x04, 0xb2, 0x94, 0x32, 0xfa, + 0x08, 0xf0, 0x29, 0x23, 0x09, 0xe1, 0x05, 0xc5, 0x1b, 0x84, 0xa0, 0x2d, 0x53, 0x5c, 0xc3, 0x33, + 0xc6, 0x4e, 0xa8, 0xd6, 0x68, 0x08, 0x16, 0x2e, 0x8a, 0x2c, 0x77, 0x5b, 0x9e, 0x39, 0x76, 0x42, + 0x1d, 0x8c, 0xee, 0x4d, 0xb0, 0x6f, 0xe8, 0x92, 0xe3, 0x62, 0x9b, 0x11, 0xf4, 0x0a, 0x4e, 0x18, + 0xa6, 0x3c, 0xca, 0xf7, 0x4a, 0xe5, 0x30, 0x90, 0x6a, 0x9d, 0x36, 0x04, 0x8b, 0x0b, 0x1e, 0x13, + 0xb7, 0xa5, 0xa8, 0x0e, 0xd0, 0x63, 0x30, 0x79, 0xc9, 0x5c, 0x53, 0x69, 0x72, 0x89, 0x9e, 0x83, + 0xcd, 0x4b, 0x16, 0xa5, 0x99, 0x10, 0x77, 0x6e, 0x5b, 0xe9, 0x3d, 0x5e, 0xb2, 0x40, 0xc6, 0xe8, + 0x19, 0x74, 0x25, 0x24, 0x34, 0x71, 0x2d, 0x85, 0x3a, 0xbc, 0x64, 0x17, 0x34, 0x41, 0x23, 0x18, + 0x54, 0xa0, 0xda, 0xd9, 0x51, 0xb8, 0xaf, 0xb1, 0xde, 0x7c, 0x0e, 0xfd, 0x7d, 0x0e, 0x4d, 0x76, + 0x6e, 0xd7, 0x33, 0xc6, 0x56, 0x68, 0xeb, 0x8c, 0x79, 0xb2, 0x43, 0x4f, 0x41, 0xba, 0x45, 0xd9, + 0xca, 0xed, 0x55, 0x47, 0x2c, 0x59, 0xb8, 0x42, 0x1e, 0x38, 0x5a, 0xae, 0x9c, 0x6d, 0x05, 0x41, + 0x41, 0x6d, 0xfc, 0x02, 0xa0, 0xca, 0x90, 0xbe, 0xa0, 0x7c, 0x7b, 0x8a, 0x4b, 0xdb, 0x09, 0x3c, + 0xc9, 0xc8, 0x77, 0x11, 0xe3, 0x82, 0x0a, 0x1e, 0x91, 0x54, 0xc4, 0xab, 0x28, 0x5d, 0xbb, 0x7d, + 0x65, 0x73, 0x5a, 0xa3, 0x0b, 0x49, 0x82, 0x35, 0x7a, 0x0b, 0x0d, 0x31, 0x4a, 0xd7, 0xb2, 0xb1, + 0xae, 0xa3, 0xb2, 0x1f, 0xd5, 0x20, 0x58, 0xdf, 0xd0, 0xa5, 0x6c, 0xaa, 0x32, 0x74, 0x07, 0x9e, + 0x31, 0x36, 0x43, 0x1d, 0xa0, 0x4b, 0x18, 0x72, 0xc1, 0xa3, 0xa6, 0x8b, 0x3a, 0xf9, 0x89, 0x67, + 0x8c, 0xfb, 0xef, 0xcf, 0x26, 0x6a, 0xfa, 0x93, 0x6b, 0xc1, 0xc3, 0xda, 0x4e, 0x26, 0x84, 0x88, + 0x1f, 0x68, 0x23, 0x06, 0xe8, 0x30, 0x53, 0x0e, 0xbd, 0x61, 0x8f, 0x37, 0x4b, 0x35, 0x74, 0x2b, + 0x1c, 0xd4, 0xea, 0x74, 0xb3, 0x44, 0xef, 0xfe, 0x73, 0x12, 0x7d, 0x07, 0x8e, 0xfd, 0xdc, 0x2f, + 0x03, 0x5e, 0xd6, 0x97, 0xb2, 0xa6, 0x73, 0x7e, 0x27, 0x32, 0xa6, 0x96, 0x75, 0xd5, 0x46, 0xb3, + 0xea, 0x33, 0xe8, 0xfd, 0x6b, 0xae, 0xf6, 0xef, 0x92, 0xaa, 0xa5, 0x1e, 0x38, 0x7b, 0xa4, 0xba, + 0xa9, 0xaf, 0x1b, 0x54, 0x58, 0x36, 0xf2, 0xb0, 0x9e, 0xf6, 0xb1, 0x7a, 0x5e, 0x43, 0x63, 0x04, + 0x51, 0x82, 0x0b, 0x5c, 0xdd, 0xc3, 0xc6, 0xee, 0xcf, 0xb8, 0xc0, 0xa3, 0x4b, 0x70, 0xae, 0x4b, + 0xf6, 0x30, 0x8f, 0x64, 0xb6, 0x05, 0x3b, 0x16, 0x4c, 0x8f, 0x6d, 0x66, 0xcb, 0xee, 0x04, 0xf2, + 0x19, 0x07, 0xc6, 0xd7, 0x37, 0x4b, 0x5a, 0xac, 0xb6, 0x8b, 0x49, 0x2c, 0x98, 0x3f, 0x9f, 0x5d, + 0xf9, 0x34, 0x21, 0x8c, 0xee, 0xfc, 0x23, 0xcf, 0xff, 0x47, 0xcb, 0x9c, 0xde, 0xde, 0xfe, 0x6c, + 0x59, 0x53, 0x19, 0xfd, 0xae, 0xbe, 0x7f, 0x5a, 0xa7, 0xea, 0xfb, 0xed, 0x4b, 0x30, 0xbb, 0x22, + 0x05, 0x96, 0x15, 0xdd, 0x57, 0x6c, 0xd1, 0x51, 0x7f, 0x16, 0x1f, 0xfe, 0x06, 0x00, 0x00, 0xff, + 0xff, 0x2e, 0xc8, 0xaa, 0x1d, 0x4e, 0x04, 0x00, 0x00, +} diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/aries/cred.proto b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/cred.proto new file mode 100644 index 000000000..17a120fe5 --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/cred.proto @@ -0,0 +1,65 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +syntax = "proto3"; + +option go_package = "github.com/IBM/idemix/bccsp/schemes/aries"; + +package aries; + +// Credential specifies a credential object +message Credential { + bytes cred = 1; + repeated bytes attrs = 2; +} + +// Signature is a PoK of a BBS+ signature (a credential) +message Signature { + bytes main_signature = 1; + bytes nonce = 2; + bytes nym = 3; + bytes nym_proof = 4; + bytes nym_eid = 5; + bytes nym_eid_proof = 6; + int32 nym_eid_idx = 7; + bytes nym_rh = 8; + bytes nym_rh_proof = 9; + int32 nym_rh_idx = 10; + + bytes revocation_epoch_pk = 11; + bytes revocation_pk_sig = 12; + int64 epoch = 13; + + NonRevocationProof non_revocation_proof = 14; +} + +// NonRevocationProof contains proof that the credential is not revoked +message NonRevocationProof { + int32 revocation_alg = 1; + bytes non_revocation_proof = 2; +} + +message CredentialRevocationInformation { + // epoch contains the epoch (time window) in which this CRI is valid + int64 epoch = 1; + + // epoch_pk is the public key that is used by the revocation authority in this epoch + bytes epoch_pk = 2; + + // epoch_pk_sig is a signature on the EpochPK valid under the revocation authority's long term key + bytes epoch_pk_sig = 3; + + // revocation_alg denotes which revocation algorithm is used + int32 revocation_alg = 4; + + // revocation_data contains data specific to the revocation algorithm used + bytes revocation_data = 5; +} + +message NymSignature { + bytes main_signature = 1; + bytes nonce = 2; +} \ No newline at end of file diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/aries/credrequest.go b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/credrequest.go new file mode 100644 index 000000000..fbe54c47a --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/credrequest.go @@ -0,0 +1,76 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package aries + +import ( + "fmt" + + "github.com/IBM/idemix/bccsp/types" + math "github.com/IBM/mathlib" + "github.com/golang/protobuf/proto" + "github.com/pkg/errors" +) + +type CredRequest struct { + Curve *math.Curve +} + +// Sign creates a new Credential Request, the first message of the interactive credential issuance protocol +// (from user to issuer) +func (c *CredRequest) Blind(sk *math.Zr, key types.IssuerPublicKey, nonce []byte) ([]byte, []byte, error) { + ipk, ok := key.(*IssuerPublicKey) + if !ok { + return nil, nil, errors.Errorf("invalid issuer public key, expected *IssuerPublicKey, got [%T]", ipk) + } + + zrs := make([]*math.Zr, ipk.N+1) + zrs[UserSecretKeyIndex] = sk + + blindedMsg, err := BlindMessagesZr(zrs, ipk.PK, 1, nonce, c.Curve) + if err != nil { + return nil, nil, fmt.Errorf("BlindMessagesZr failed [%w]", err) + } + + return blindedMsg.Bytes(), blindedMsg.S.Bytes(), nil +} + +// Verify verifies the credential request +func (c *CredRequest) BlindVerify(credRequest []byte, key types.IssuerPublicKey, nonce []byte) error { + ipk, ok := key.(*IssuerPublicKey) + if !ok { + return errors.Errorf("invalid issuer public key, expected *IssuerPublicKey, got [%T]", ipk) + } + + bitmap := make([]bool, ipk.N+1) + bitmap[UserSecretKeyIndex] = true + + blindedMsg, err := ParseBlindedMessages(credRequest, c.Curve) + if err != nil { + return fmt.Errorf("ParseBlindedMessages failed [%w]", err) + } + + return VerifyBlinding(bitmap, blindedMsg.C, blindedMsg.PoK, ipk.PK, nonce) +} + +// Unblind takes a blinded signature and a blinding and produces a standard signature +func (c *CredRequest) Unblind(signature, blinding []byte) ([]byte, error) { + S := c.Curve.NewZrFromBytes(blinding) + + credential := &Credential{} + err := proto.Unmarshal(signature, credential) + if err != nil { + return nil, fmt.Errorf("proto.Unmarshal failed [%w]", err) + } + + sig, err := UnblindSign(credential.Cred, S, c.Curve) + if err != nil { + return nil, fmt.Errorf("bls.UnblindSign failed [%w]", err) + } + + credential.Cred = sig + + return proto.Marshal(credential) +} diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/aries/issuer.go b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/issuer.go new file mode 100644 index 000000000..bc935aeb4 --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/issuer.go @@ -0,0 +1,134 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package aries + +import ( + "crypto/rand" + "crypto/sha256" + "fmt" + + "github.com/IBM/idemix/bccsp/types" + "github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub" +) + +// TODO: +// * expose curve from aries so we can use always that curve + +// UserSecretKeyIndex is the index of `sk` among the attributes +const UserSecretKeyIndex = 0 + +// IssuerPublicKey is the issuer public key +type IssuerPublicKey struct { + PK *bbs12381g2pub.PublicKey + PKwG *bbs12381g2pub.PublicKeyWithGenerators + // N is the number of attributes; it *does not* include the user secret key + N int +} + +// Bytes returns the byte representation of this key +func (i *IssuerPublicKey) Bytes() ([]byte, error) { + return i.PK.Marshal() +} + +// Hash returns the hash representation of this key. +// The output is supposed to be collision-resistant +func (i *IssuerPublicKey) Hash() []byte { + return i.PK.PointG2.Compressed() +} + +// IssuerPublicKey is the issuer secret key +type IssuerSecretKey struct { + IssuerPublicKey + SK *bbs12381g2pub.PrivateKey +} + +// Bytes returns the byte representation of this key +func (i *IssuerSecretKey) Bytes() ([]byte, error) { + return i.SK.Marshal() +} + +// Public returns the corresponding public key +func (i *IssuerSecretKey) Public() types.IssuerPublicKey { + return &i.IssuerPublicKey +} + +// Issuer is a local interface to decouple from the idemix implementation +type Issuer struct { +} + +// NewKey generates a new idemix issuer key w.r.t the passed attribute names. +func (i *Issuer) NewKey(AttributeNames []string) (types.IssuerSecretKey, error) { + seed := make([]byte, 32) + + _, err := rand.Read(seed) + if err != nil { + return nil, fmt.Errorf("rand.Read failed [%w]", err) + } + + PK, SK, err := bbs12381g2pub.GenerateKeyPair(sha256.New, seed) + if err != nil { + return nil, fmt.Errorf("GenerateKeyPair failed [%w]", err) + } + + PKwG, err := PK.ToPublicKeyWithGenerators(len(AttributeNames) + 1) + if err != nil { + return nil, fmt.Errorf("ToPublicKeyWithGenerators failed [%w]", err) + } + + return &IssuerSecretKey{ + SK: SK, + IssuerPublicKey: IssuerPublicKey{ + PK: PK, + PKwG: PKwG, + N: len(AttributeNames), + }, + }, nil +} + +// NewPublicKeyFromBytes converts the passed bytes to an Issuer key +// It makes sure that the so obtained key has the passed attributes, if specified +func (i *Issuer) NewKeyFromBytes(raw []byte, attributes []string) (types.IssuerSecretKey, error) { + SK, err := bbs12381g2pub.UnmarshalPrivateKey(raw) + if err != nil { + return nil, fmt.Errorf("UnmarshalPrivateKey failed [%w]", err) + } + + PK := SK.PublicKey() + + PKwG, err := PK.ToPublicKeyWithGenerators(len(attributes) + 1) + if err != nil { + return nil, fmt.Errorf("ToPublicKeyWithGenerators failed [%w]", err) + } + + return &IssuerSecretKey{ + SK: SK, + IssuerPublicKey: IssuerPublicKey{ + PK: PK, + PKwG: PKwG, + N: len(attributes), + }, + }, nil +} + +// NewPublicKeyFromBytes converts the passed bytes to an Issuer public key +// It makes sure that the so obtained public key has the passed attributes, if specified +func (i *Issuer) NewPublicKeyFromBytes(raw []byte, attributes []string) (types.IssuerPublicKey, error) { + PK, err := bbs12381g2pub.UnmarshalPublicKey(raw) + if err != nil { + return nil, fmt.Errorf("UnmarshalPublicKey failed [%w]", err) + } + + PKwG, err := PK.ToPublicKeyWithGenerators(len(attributes) + 1) + if err != nil { + return nil, fmt.Errorf("ToPublicKeyWithGenerators failed [%w]", err) + } + + return &IssuerPublicKey{ + PK: PK, + PKwG: PKwG, + N: len(attributes), + }, nil +} diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/aries/nymsigner.go b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/nymsigner.go new file mode 100644 index 000000000..69c7fe982 --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/nymsigner.go @@ -0,0 +1,102 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package aries + +import ( + "fmt" + "io" + + "github.com/IBM/idemix/bccsp/types" + math "github.com/IBM/mathlib" + "github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub" + "github.com/golang/protobuf/proto" +) + +const nymSigLabel = "nym-sig" + +type NymSigner struct { + Curve *math.Curve + Rng io.Reader +} + +// Sign creates a new idemix pseudonym signature +func (s *NymSigner) Sign( + sk *math.Zr, + Nym *math.G1, + RNym *math.Zr, + key types.IssuerPublicKey, + digest []byte, +) ([]byte, error) { + ipk, ok := key.(*IssuerPublicKey) + if !ok { + return nil, fmt.Errorf("invalid issuer public key, expected *IssuerPublicKey, got [%T]", ipk) + } + + Nonce := s.Curve.NewRandomZr(s.Rng) + + commit := bbs12381g2pub.NewProverCommittingG1() + commit.Commit(ipk.PKwG.H0) + commit.Commit(ipk.PKwG.H[0]) + commitNym := commit.Finish() + + challengeBytes := []byte(nymSigLabel) + challengeBytes = append(challengeBytes, Nym.Bytes()...) + challengeBytes = append(challengeBytes, commitNym.ToBytes()...) + challengeBytes = append(challengeBytes, digest...) + + proofChallenge := bbs12381g2pub.FrFromOKM(challengeBytes) + + challengeBytes = proofChallenge.Bytes() + challengeBytes = append(challengeBytes, Nonce.Bytes()...) + proofChallenge = bbs12381g2pub.FrFromOKM(challengeBytes) + + proof := commitNym.GenerateProof(proofChallenge, []*math.Zr{RNym, sk}) + + sig := &NymSignature{ + MainSignature: proof.ToBytes(), + Nonce: Nonce.Bytes(), + } + + return proto.Marshal(sig) +} + +// Verify verifies an idemix NymSignature +func (s *NymSigner) Verify( + key types.IssuerPublicKey, + Nym *math.G1, + sigBytes, digest []byte, +) error { + ipk, ok := key.(*IssuerPublicKey) + if !ok { + return fmt.Errorf("invalid issuer public key, expected *IssuerPublicKey, got [%T]", ipk) + } + + sig := &NymSignature{} + err := proto.Unmarshal(sigBytes, sig) + if err != nil { + return fmt.Errorf("error unmarshalling signature: [%w]", err) + } + + nymProof, err := bbs12381g2pub.ParseProofG1(sig.MainSignature) + if err != nil { + return fmt.Errorf("parse nym proof: %w", err) + } + + challengeBytes := []byte(nymSigLabel) + challengeBytes = append(challengeBytes, Nym.Bytes()...) + challengeBytes = append(challengeBytes, ipk.PKwG.H0.Bytes()...) + challengeBytes = append(challengeBytes, ipk.PKwG.H[0].Bytes()...) + challengeBytes = append(challengeBytes, nymProof.Commitment.Bytes()...) + challengeBytes = append(challengeBytes, digest...) + + proofChallenge := bbs12381g2pub.FrFromOKM(challengeBytes) + + challengeBytes = proofChallenge.Bytes() + challengeBytes = append(challengeBytes, sig.Nonce...) + proofChallenge = bbs12381g2pub.FrFromOKM(challengeBytes) + + return nymProof.Verify([]*math.G1{ipk.PKwG.H0, ipk.PKwG.H[0]}, Nym, proofChallenge) +} diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/aries/revocation.go b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/revocation.go new file mode 100644 index 000000000..053de7ad2 --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/revocation.go @@ -0,0 +1,124 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package aries + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "encoding/asn1" + "fmt" + "io" + "math/big" + + weakbb "github.com/IBM/idemix/bccsp/schemes/weak-bb" + "github.com/IBM/idemix/bccsp/types" + math "github.com/IBM/mathlib" + "github.com/golang/protobuf/proto" + "github.com/pkg/errors" +) + +type RevocationAuthority struct { + Rng io.Reader + Curve *math.Curve +} + +// NewKey generates a long term signing key that will be used for revocation +func (r *RevocationAuthority) NewKey() (*ecdsa.PrivateKey, error) { + return ecdsa.GenerateKey(elliptic.P384(), rand.Reader) +} + +// NewKeyFromBytes generates a long term signing key that will be used for revocation from the passed bytes +func (r *RevocationAuthority) NewKeyFromBytes(raw []byte) (*ecdsa.PrivateKey, error) { + priv := &ecdsa.PrivateKey{} + priv.D = new(big.Int).SetBytes(raw) + priv.PublicKey.Curve = elliptic.P384() + priv.PublicKey.X, priv.PublicKey.Y = elliptic.P384().ScalarBaseMult(priv.D.Bytes()) + + return priv, nil +} + +// Sign creates the Credential Revocation Information for a certain time period (epoch). +// Users can use the CRI to prove that they are not revoked. +func (r *RevocationAuthority) Sign(key *ecdsa.PrivateKey, _ [][]byte, epoch int, alg types.RevocationAlgorithm) ([]byte, error) { + if key == nil { + return nil, errors.Errorf("CreateCRI received nil input") + } + + cri := &CredentialRevocationInformation{} + cri.RevocationAlg = int32(alg) + cri.Epoch = int64(epoch) + + if alg == types.AlgNoRevocation { + // put a dummy PK in the proto + cri.EpochPk = r.Curve.GenG2.Bytes() + } else { + // create epoch key + _, epochPk := weakbb.WbbKeyGen(r.Curve, r.Rng) + cri.EpochPk = epochPk.Bytes() + } + + // sign epoch + epoch key with long term key + bytesToSign, err := proto.Marshal(cri) + if err != nil { + return nil, errors.Wrap(err, "failed to marshal CRI") + } + + digest := sha256.Sum256(bytesToSign) + + cri.EpochPkSig, err = key.Sign(rand.Reader, digest[:], nil) + if err != nil { + return nil, err + } + + if alg == types.AlgNoRevocation { + return proto.Marshal(cri) + } else { + return nil, errors.Errorf("the specified revocation algorithm is not supported.") + } +} + +// Verify verifies that the revocation PK for a certain epoch is valid, +// by checking that it was signed with the long term revocation key. +// Note that even if we use no revocation (i.e., alg = ALG_NO_REVOCATION), we need +// to verify the signature to make sure the issuer indeed signed that no revocation +// is used in this epoch. +func (r *RevocationAuthority) Verify(pk *ecdsa.PublicKey, criRaw []byte, epoch int, alg types.RevocationAlgorithm) error { + if pk == nil { + return fmt.Errorf("CreateCRI received nil input") + } + + cri := &CredentialRevocationInformation{} + err := proto.Unmarshal(criRaw, cri) + if err != nil { + return err + } + + epochPkSig := cri.EpochPkSig + cri.EpochPkSig = nil + cri.Epoch = int64(epoch) + cri.RevocationAlg = int32(alg) + + bytesToVer, err := proto.Marshal(cri) + if err != nil { + return err + } + + digest := sha256.Sum256(bytesToVer) + + var sig struct{ R, S *big.Int } + if _, err := asn1.Unmarshal(epochPkSig, &sig); err != nil { + return errors.Wrap(err, "failed unmashalling signature") + } + + if !ecdsa.Verify(pk, digest[:], sig.R, sig.S) { + return errors.Errorf("EpochPKSig invalid") + } + + return nil +} diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/aries/signer.go b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/signer.go new file mode 100644 index 000000000..28c3f1172 --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/signer.go @@ -0,0 +1,774 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package aries + +import ( + "crypto/ecdsa" + "fmt" + "io" + + "github.com/IBM/idemix/bccsp/types" + math "github.com/IBM/mathlib" + "github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub" + "github.com/golang/protobuf/proto" +) + +// AttributeIndexInNym is the index of the blinding factor of the attribute in a Nym commitment +const AttributeIndexInNym = 1 + +// IndexOffsetVC2Attributes is the index of the attributes in VC2 +const IndexOffsetVC2Attributes = 2 + +const signLabel = "sign" +const signWithEidNymLabel = "signWithEidNym" +const signWithEidNymRhNymLabel = "signWithEidNymRhNym" // When the revocation handle is present the enrollment id must also be present + +type Signer struct { + Curve *math.Curve + Rng io.Reader +} + +func (s *Signer) getPoKOfSignature( + credBytes []byte, + attributes []types.IdemixAttribute, + sk *math.Zr, + ipk *bbs12381g2pub.PublicKeyWithGenerators, +) (*bbs12381g2pub.PoKOfSignature, []*bbs12381g2pub.SignatureMessage, error) { + credential := &Credential{} + err := proto.Unmarshal(credBytes, credential) + if err != nil { + return nil, nil, fmt.Errorf("proto.Unmarshal failed [%w]", err) + } + + signature, err := bbs12381g2pub.ParseSignature(credential.Cred) + if err != nil { + return nil, nil, fmt.Errorf("parse signature: %w", err) + } + + messagesFr := credential.toSignatureMessage(sk, s.Curve) + + pokOS, err := bbs12381g2pub.NewPoKOfSignature(signature, messagesFr, revealedAttributesIndex(attributes), ipk) + if err != nil { + return nil, nil, fmt.Errorf("bbs12381g2pub.NewPoKOfSignature error: %w", err) + } + + return pokOS, messagesFr, nil +} + +func (s *Signer) getChallengeHash( + pokSignature *bbs12381g2pub.PoKOfSignature, + Nym *math.G1, + commitNym *math.G1, + eid *attributeCommitment, + rh *attributeCommitment, + msg []byte, + sigType types.SignatureType, +) (*math.Zr, *math.Zr) { + + // hash the signature type first + var challengeBytes []byte + switch sigType { + case types.Standard: + challengeBytes = []byte(signLabel) + case types.EidNym: + challengeBytes = []byte(signWithEidNymLabel) + case types.EidNymRhNym: + challengeBytes = []byte(signWithEidNymRhNymLabel) + default: + panic("programming error") + } + + // hash the main proof + challengeBytes = append(challengeBytes, pokSignature.ToBytes()...) + + // hash the Nym and t-value + challengeBytes = append(challengeBytes, Nym.Bytes()...) + challengeBytes = append(challengeBytes, commitNym.Bytes()...) + + // hash the NymEid and t-value + if sigType == types.EidNym || sigType == types.EidNymRhNym { + challengeBytes = append(challengeBytes, eid.comm.Bytes()...) + challengeBytes = append(challengeBytes, eid.proof.Commitment.Bytes()...) + } + + // hash the NymEid and t-value + if sigType == types.EidNymRhNym { + challengeBytes = append(challengeBytes, rh.comm.Bytes()...) + challengeBytes = append(challengeBytes, rh.proof.Commitment.Bytes()...) + } + + // hash the nonce + proofNonce := bbs12381g2pub.ParseProofNonce(msg) + proofNonceBytes := proofNonce.ToBytes() + challengeBytes = append(challengeBytes, proofNonceBytes...) + + c := bbs12381g2pub.FrFromOKM(challengeBytes) + + Nonce := s.Curve.NewRandomZr(s.Rng) + + challengeBytes = c.Bytes() + challengeBytes = append(challengeBytes, Nonce.Bytes()...) + + return bbs12381g2pub.FrFromOKM(challengeBytes), Nonce +} + +func (s *Signer) packageProof( + attributes []types.IdemixAttribute, + Nym *math.G1, + proof *bbs12381g2pub.PoKOfSignatureProof, + proofNym *bbs12381g2pub.ProofG1, + nymEid *attributeCommitment, + proofNymEid *bbs12381g2pub.ProofG1, + rhNym *attributeCommitment, + proofRhNym *bbs12381g2pub.ProofG1, + cri *CredentialRevocationInformation, + nonce *math.Zr, +) ([]byte, error) { + payload := bbs12381g2pub.NewPoKPayload(len(attributes)+1, revealedAttributesIndex(attributes)) + + payloadBytes, err := payload.ToBytes() + if err != nil { + return nil, fmt.Errorf("derive proof: paylod to bytes: %w", err) + } + + signatureProofBytes := append(payloadBytes, proof.ToBytes()...) + + sig := &Signature{ + MainSignature: signatureProofBytes, + Nonce: nonce.Bytes(), + Nym: Nym.Bytes(), + NymProof: proofNym.ToBytes(), + RevocationEpochPk: cri.EpochPk, + RevocationPkSig: cri.EpochPkSig, + Epoch: cri.Epoch, + NonRevocationProof: &NonRevocationProof{ + RevocationAlg: cri.RevocationAlg, + }, + } + + if nymEid != nil { + sig.NymEid = nymEid.comm.Bytes() + sig.NymEidProof = proofNymEid.ToBytes() + sig.NymEidIdx = int32(nymEid.index) + } + + if rhNym != nil { + sig.NymRh = rhNym.comm.Bytes() + sig.NymRhProof = proofRhNym.ToBytes() + sig.NymRhIdx = int32(rhNym.index) + } + + return proto.Marshal(sig) +} + +func (s *Signer) getCommitNym( + ipk *IssuerPublicKey, + pokSignature *bbs12381g2pub.PoKOfSignature, +) *bbs12381g2pub.ProverCommittedG1 { + + // Nym is H0^{RNym} \cdot H[0]^{sk} + + commit := bbs12381g2pub.NewProverCommittingG1() + commit.Commit(ipk.PKwG.H0) + commit.Commit(ipk.PKwG.H[UserSecretKeyIndex]) + // we force the same blinding factor used in PokVC2 to prove equality. + // 1) commit.BlindingFactors[1] is the blinding factor for the sk in the Nym + // H0^{RNym} \cdot H[0]^{sk} + // 2) pokSignature.PokVC2.BlindingFactors[2] is the blinding factor for the sk in + // D * (-r3~) + Q_1 * s~ + H_j1 * m~_j1 + ... + H_jU * m~_jU + // index 0 is for D, index 1 is for s~ and index 2 is for the first message (which is the sk) + commit.BlindingFactors[AttributeIndexInNym] = pokSignature.PokVC2.BlindingFactors[IndexOffsetVC2Attributes+UserSecretKeyIndex] + + return commit.Finish() +} + +type attributeCommitment struct { + index int + proof *bbs12381g2pub.ProverCommittedG1 + comm *math.G1 + r *math.Zr +} + +func safeRhNymAuditDataAccess(metadata *types.IdemixSignerMetadata) *types.AttrNymAuditData { + if metadata == nil { + return nil + } + + return metadata.RhNymAuditData +} + +func rhAttrCommitmentEnabled(sigType types.SignatureType) bool { + return sigType == types.EidNymRhNym +} + +func safeNymEidAuditDataAccess(metadata *types.IdemixSignerMetadata) *types.AttrNymAuditData { + if metadata == nil { + return nil + } + + return metadata.EidNymAuditData +} + +func nymEidAttrCommitmentEnabled(sigType types.SignatureType) bool { + return sigType != types.Standard +} + +func (s *Signer) getAttributeCommitment( + ipk *IssuerPublicKey, + pokSignature *bbs12381g2pub.PoKOfSignature, + attr *math.Zr, + idxInBases int, + enabled bool, + auditData *types.AttrNymAuditData, +) (*attributeCommitment, error) { + + if !enabled { + return nil, nil + } + + var Nym *math.G1 + var R *math.Zr + + cb := bbs12381g2pub.NewCommitmentBuilder(2) + + if auditData != nil { + if !attr.Equals(auditData.Attr) { + return nil, fmt.Errorf("attribute supplied in metadata differs from signed") + } + + R = auditData.Rand + + cb.Add(ipk.PKwG.H0, R) + cb.Add(ipk.PKwG.H[idxInBases], auditData.Attr) + Nym = cb.Build() + + if !auditData.Nym.Equals(Nym) { + return nil, fmt.Errorf("nym supplied in metadata cannot be recomputed") + } + } else { + R = s.Curve.NewRandomZr(s.Rng) + + cb.Add(ipk.PKwG.H0, R) + cb.Add(ipk.PKwG.H[idxInBases], attr) + Nym = cb.Build() + } + + attrIndexInCommitment, err := s.indexOfAttributeInCommitment(pokSignature.PokVC2, idxInBases, ipk.PKwG) + if err != nil { + return nil, fmt.Errorf("error determining index for attribute: %w", err) + } + + commit := bbs12381g2pub.NewProverCommittingG1() + commit.Commit(ipk.PKwG.H0) + commit.Commit(ipk.PKwG.H[idxInBases]) + + // we force the same blinding factor used in PokVC2 to prove equality. + commit.BlindingFactors[AttributeIndexInNym] = pokSignature.PokVC2.BlindingFactors[attrIndexInCommitment] + + return &attributeCommitment{ + index: attrIndexInCommitment, + proof: commit.Finish(), + comm: Nym, + r: R, + }, nil +} + +func (s *Signer) indexOfAttributeInCommitment( + c *bbs12381g2pub.ProverCommittedG1, + indexInPk int, + ipk *bbs12381g2pub.PublicKeyWithGenerators, +) (int, error) { + + // this is the base used in the public key for the attribute; no +1 since we assume that the caller has already catered for that + base := ipk.H[indexInPk] + + for i, h_i := range c.Bases { + if base.Equals(h_i) { + return i, nil + } + } + + return -1, fmt.Errorf("attribute not found") +} + +// Sign creates a new idemix signature +func (s *Signer) Sign( + credBytes []byte, + sk *math.Zr, + Nym *math.G1, + RNym *math.Zr, + key types.IssuerPublicKey, + attributes []types.IdemixAttribute, + msg []byte, + rhIndex, eidIndex int, + criRaw []byte, + sigType types.SignatureType, + metadata *types.IdemixSignerMetadata, +) ([]byte, *types.IdemixSignerMetadata, error) { + + /////////////// + // arg check // + /////////////// + + if sigType == types.EidNym && + attributes[eidIndex].Type != types.IdemixHiddenAttribute { + return nil, nil, fmt.Errorf("cannot create idemix signature: disclosure of enrollment ID requested for EidNym signature") + } + + if sigType == types.EidNymRhNym && + (attributes[eidIndex].Type != types.IdemixHiddenAttribute || + attributes[rhIndex].Type != types.IdemixHiddenAttribute) { + return nil, nil, fmt.Errorf("cannot create idemix signature: disclosure of enrollment ID or RH requested for EidNymRhNym signature") + } + + ipk, ok := key.(*IssuerPublicKey) + if !ok { + return nil, nil, fmt.Errorf("invalid issuer public key, expected *IssuerPublicKey, got [%T]", ipk) + } + + /////////////////////// + // handle revocation // + /////////////////////// + + cri := &CredentialRevocationInformation{} + err := proto.Unmarshal(criRaw, cri) + if err != nil { + return nil, nil, fmt.Errorf("failed unmarshalling credential revocation information [%w]", err) + } + + // if we add any other revocation algorithm, we need to change the challenge hash + if cri.RevocationAlg != int32(types.AlgNoRevocation) { + return nil, nil, fmt.Errorf("Unsupported revocation algorithm") + } + + ////////////////////////////////// + // Generate main PoK (1st move) // + ////////////////////////////////// + + pokSignature, messagesFr, err := s.getPoKOfSignature(credBytes, attributes, sk, ipk.PKwG) + if err != nil { + return nil, nil, err + } + + ////////////////// + // Handling Nym // + ////////////////// + + commitNym := s.getCommitNym(ipk, pokSignature) + + /////////////////// + // Handle NymEID // + /////////////////// + + // increment the index to cater for the first hidden index for `sk` + eidIndex++ + + nymEid, err := s.getAttributeCommitment(ipk, pokSignature, messagesFr[eidIndex].FR, eidIndex, nymEidAttrCommitmentEnabled(sigType), safeNymEidAuditDataAccess(metadata)) + if err != nil { + return nil, nil, err + } + + /////////////////// + // Handle RhNym // + /////////////////// + + // increment the index to cater for the first hidden index for `sk` + rhIndex++ + + rhNym, err := s.getAttributeCommitment(ipk, pokSignature, messagesFr[rhIndex].FR, rhIndex, rhAttrCommitmentEnabled(sigType), safeRhNymAuditDataAccess(metadata)) + if err != nil { + return nil, nil, err + } + + /////////////////////// + // Get the challenge // + /////////////////////// + + proofChallenge, Nonce := s.getChallengeHash(pokSignature, Nym, commitNym.Commitment, nymEid, rhNym, msg, sigType) + + //////////////////////// + // Generate responses // + //////////////////////// + + // 1) main + proof := pokSignature.GenerateProof(proofChallenge) + // 2) Nym + proofNym := commitNym.GenerateProof(proofChallenge, []*math.Zr{RNym, sk}) + // 3) NymEid + var proofNymEid *bbs12381g2pub.ProofG1 + if nymEid != nil { + proofNymEid = nymEid.proof.GenerateProof(proofChallenge, []*math.Zr{nymEid.r, messagesFr[eidIndex].FR}) + } + // 4) RhNym + var proofRhNym *bbs12381g2pub.ProofG1 + if rhNym != nil { + proofRhNym = rhNym.proof.GenerateProof(proofChallenge, []*math.Zr{rhNym.r, messagesFr[rhIndex].FR}) + } + + /////////////////// + // Package proof // + /////////////////// + + sigBytes, err := s.packageProof(attributes, Nym, proof, proofNym, nymEid, proofNymEid, rhNym, proofRhNym, cri, Nonce) + if err != nil { + return nil, nil, err + } + + var m *types.IdemixSignerMetadata + if sigType == types.EidNym { + m = &types.IdemixSignerMetadata{ + EidNymAuditData: &types.AttrNymAuditData{ + Nym: nymEid.comm, + Rand: nymEid.r, + Attr: messagesFr[eidIndex].FR, + }, + } + } + + if sigType == types.EidNymRhNym { + m = &types.IdemixSignerMetadata{ + EidNymAuditData: &types.AttrNymAuditData{ + Nym: nymEid.comm, + Rand: nymEid.r, + Attr: messagesFr[eidIndex].FR, + }, + RhNymAuditData: &types.AttrNymAuditData{ + Nym: rhNym.comm, + Rand: rhNym.r, + Attr: messagesFr[rhIndex].FR, + }, + } + } + + return sigBytes, m, nil +} + +// Verify verifies an idemix signature. +func (s *Signer) Verify( + key types.IssuerPublicKey, + signature, msg []byte, + attributes []types.IdemixAttribute, + rhIndex, eidIndex int, + _ *ecdsa.PublicKey, + _ int, + verType types.VerificationType, + meta *types.IdemixSignerMetadata, +) error { + ipk, ok := key.(*IssuerPublicKey) + if !ok { + return fmt.Errorf("invalid issuer public key, expected *IssuerPublicKey, got [%T]", ipk) + } + + sig := &Signature{} + err := proto.Unmarshal(signature, sig) + if err != nil { + return fmt.Errorf("proto.Unmarshal error: %w", err) + } + + if sig.NonRevocationProof.RevocationAlg != int32(types.AlgNoRevocation) { + return fmt.Errorf("unsupported revocation algorithm") + } + + if verType == types.ExpectEidNym && + (len(sig.NymEid) == 0 || len(sig.NymEidProof) == 0) { + return fmt.Errorf("no EidNym provided but ExpectEidNym required") + } + + if verType == types.ExpectEidNymRhNym { + if len(sig.NymEid) == 0 || len(sig.NymEidProof) == 0 { + return fmt.Errorf("no EidNym provided but ExpectEidNymRhNym required") + } + if len(sig.NymRh) == 0 || len(sig.NymRhProof) == 0 { + return fmt.Errorf("no RhNym provided but ExpectEidNymRhNym required") + } + } + + if verType == types.ExpectStandard { + if len(sig.NymRh) != 0 || len(sig.NymRhProof) != 0 { + return fmt.Errorf("RhNym available but ExpectStandard required") + } + if len(sig.NymEid) != 0 || len(sig.NymEidProof) != 0 { + return fmt.Errorf("EidNym available but ExpectStandard required") + } + } + + verifyRHNym := (verType == types.BestEffort && sig.NymRh != nil) || verType == types.ExpectEidNymRhNym + verifyEIDNym := (verType == types.BestEffort && sig.NymEid != nil) || verType == types.ExpectEidNym || verType == types.ExpectEidNymRhNym || verifyRHNym + + messages := attributesToSignatureMessage(nil, attributes, s.Curve) + + payload, err := bbs12381g2pub.ParsePoKPayload(sig.MainSignature) + if err != nil { + return fmt.Errorf("parse signature proof: %w", err) + } + + signatureProof, err := bbs12381g2pub.ParseSignatureProof(sig.MainSignature[payload.LenInBytes():]) + if err != nil { + return fmt.Errorf("parse signature proof: %w", err) + } + + if len(payload.Revealed) > len(messages) { + return fmt.Errorf("payload revealed bigger from messages") + } + + revealedMessages := make(map[int]*bbs12381g2pub.SignatureMessage) + for i := range payload.Revealed { + revealedMessages[payload.Revealed[i]] = messages[i] + } + + Nym, err := s.Curve.NewG1FromBytes(sig.Nym) + if err != nil { + return fmt.Errorf("parse nym commit: %w", err) + } + + nymProof, err := bbs12381g2pub.ParseProofG1(sig.NymProof) + if err != nil { + return fmt.Errorf("parse nym proof: %w", err) + } + + var nymEidProof *bbs12381g2pub.ProofG1 + var NymEid *math.G1 + if verifyEIDNym { + nymEidProof, err = bbs12381g2pub.ParseProofG1(sig.NymEidProof) + if err != nil { + return fmt.Errorf("parse nym proof: %w", err) + } + + NymEid, err = s.Curve.NewG1FromBytes(sig.NymEid) + if err != nil { + return fmt.Errorf("parse nym commit: %w", err) + } + } + + var rhNymProof *bbs12381g2pub.ProofG1 + var RhNym *math.G1 + if verifyRHNym { + rhNymProof, err = bbs12381g2pub.ParseProofG1(sig.NymRhProof) + if err != nil { + return fmt.Errorf("parse rh proof: %w", err) + } + + RhNym, err = s.Curve.NewG1FromBytes(sig.NymRh) + if err != nil { + return fmt.Errorf("parse rh commit: %w", err) + } + } + + //////////////////////// + // Hash the challenge // + //////////////////////// + + // hash the signature type first + var challengeBytes []byte + if verifyRHNym { + challengeBytes = []byte(signWithEidNymRhNymLabel) + } else if verifyEIDNym { + challengeBytes = []byte(signWithEidNymLabel) + } else { + challengeBytes = []byte(signLabel) + } + + challengeBytes = append(challengeBytes, signatureProof.GetBytesForChallenge(revealedMessages, ipk.PKwG)...) + + challengeBytes = append(challengeBytes, sig.Nym...) + challengeBytes = append(challengeBytes, nymProof.Commitment.Bytes()...) + + if verifyEIDNym { + challengeBytes = append(challengeBytes, sig.NymEid...) + challengeBytes = append(challengeBytes, nymEidProof.Commitment.Bytes()...) + } + + if verifyRHNym { + challengeBytes = append(challengeBytes, sig.NymRh...) + challengeBytes = append(challengeBytes, rhNymProof.Commitment.Bytes()...) + } + + proofNonce := bbs12381g2pub.ParseProofNonce(msg) + proofNonceBytes := proofNonce.ToBytes() + challengeBytes = append(challengeBytes, proofNonceBytes...) + proofChallenge := bbs12381g2pub.FrFromOKM(challengeBytes) + + challengeBytes = proofChallenge.Bytes() + challengeBytes = append(challengeBytes, sig.Nonce...) + proofChallenge = bbs12381g2pub.FrFromOKM(challengeBytes) + + ////////////////////// + // Verify responses // + ////////////////////// + + // audit eid nym if data provided and verification requested + if (verifyEIDNym || verifyRHNym) && meta != nil { + if meta.EidNymAuditData != nil { + ne := ipk.PKwG.H[eidIndex+1].Mul2( + meta.EidNymAuditData.Attr, + ipk.PKwG.H0, meta.EidNymAuditData.Rand) + + if !ne.Equals(NymEid) { + return fmt.Errorf("signature invalid: nym eid validation failed, does not match regenerated nym eid") + } + + if meta.EidNymAuditData.Nym != nil && !NymEid.Equals(meta.EidNymAuditData.Nym) { + return fmt.Errorf("signature invalid: nym eid validation failed, does not match metadata") + } + } + } + + // audit rh nym if data provided and verification requested + if verifyRHNym && meta != nil { + if meta.RhNymAuditData != nil { + rn := ipk.PKwG.H[rhIndex+1].Mul2( + meta.RhNymAuditData.Attr, + ipk.PKwG.H0, meta.RhNymAuditData.Rand, + ) + + if !rn.Equals(RhNym) { + return fmt.Errorf("signature invalid: nym rh validation failed, does not match regenerated nym rh") + } + + if meta.RhNymAuditData.Nym != nil && !RhNym.Equals(meta.RhNymAuditData.Nym) { + return fmt.Errorf("signature invalid: nym rh validation failed, does not match metadata") + } + } + } + + // verify that `sk` in the Nym is the same as the one in the signature + if !nymProof.Responses[AttributeIndexInNym].Equals(signatureProof.ProofVC2.Responses[IndexOffsetVC2Attributes+UserSecretKeyIndex]) { + return fmt.Errorf("failed equality proof for sk") + } + + // verify the proof of knowledge of the Nym + err = nymProof.Verify([]*math.G1{ipk.PKwG.H0, ipk.PKwG.H[UserSecretKeyIndex]}, Nym, proofChallenge) + if err != nil { + return fmt.Errorf("verify nym proof: %w", err) + } + + if verifyEIDNym { + // verify that eid in the NymEid is the same as the one in the signature + if !nymEidProof.Responses[AttributeIndexInNym].Equals(signatureProof.ProofVC2.Responses[sig.NymEidIdx]) { + return fmt.Errorf("failed equality proof for eid") + } + + // verify the proof of knowledge of the Nym + err = nymEidProof.Verify([]*math.G1{ipk.PKwG.H0, ipk.PKwG.H[eidIndex+1]}, NymEid, proofChallenge) + if err != nil { + return fmt.Errorf("verify nym eid proof: %w", err) + } + } + + if verifyRHNym { + // verify that rh in the RhNym is the same as the one in the signature + if !rhNymProof.Responses[AttributeIndexInNym].Equals(signatureProof.ProofVC2.Responses[sig.NymRhIdx]) { + return fmt.Errorf("failed equality proof for rh") + } + + // verify the proof of knowledge of the Rh + err = rhNymProof.Verify([]*math.G1{ipk.PKwG.H0, ipk.PKwG.H[rhIndex+1]}, RhNym, proofChallenge) + if err != nil { + return fmt.Errorf("verify nym eid proof: %w", err) + } + } + + // verify the proof of knowledge of the signature + return signatureProof.Verify(proofChallenge, ipk.PKwG, revealedMessages, messages) +} + +// AuditNymEid permits the auditing of the nym eid generated by a signer +func (s *Signer) AuditNymEid( + key types.IssuerPublicKey, + eidIndex int, + signature []byte, + enrollmentID string, + RNymEid *math.Zr, + verType types.AuditVerificationType, +) error { + ipk, ok := key.(*IssuerPublicKey) + if !ok { + return fmt.Errorf("invalid issuer public key, expected *IssuerPublicKey, got [%T]", ipk) + } + + var NymEid *math.G1 + switch verType { + case types.AuditExpectSignature: + sig := &Signature{} + err := proto.Unmarshal(signature, sig) + if err != nil { + return fmt.Errorf("proto.Unmarshal error: %w", err) + } + + NymEid, err = s.Curve.NewG1FromBytes(sig.NymEid) + if err != nil { + return fmt.Errorf("parse nym commit: %w", err) + } + case types.AuditExpectEidNymRhNym: + fallthrough + case types.AuditExpectEidNym: + var err error + NymEid, err = s.Curve.NewG1FromBytes(signature) + if err != nil { + return fmt.Errorf("parse nym commit: %w", err) + } + default: + return fmt.Errorf("invalid audit type [%d]", verType) + } + + eidAttr := bbs12381g2pub.FrFromOKM([]byte(enrollmentID)) + + ne := ipk.PKwG.H[eidIndex+1].Mul2(eidAttr, ipk.PKwG.H0, RNymEid) + + if !ne.Equals(NymEid) { + return fmt.Errorf("eid nym does not match") + } + + return nil +} + +// AuditNymRh permits the auditing of the nym rh generated by a signer +func (s *Signer) AuditNymRh( + key types.IssuerPublicKey, + rhIndex int, + signature []byte, + revocationHandle string, + RNymRh *math.Zr, + verType types.AuditVerificationType, +) error { + ipk, ok := key.(*IssuerPublicKey) + if !ok { + return fmt.Errorf("invalid issuer public key, expected *IssuerPublicKey, got [%T]", ipk) + } + + var RhNym *math.G1 + switch verType { + case types.AuditExpectSignature: + sig := &Signature{} + err := proto.Unmarshal(signature, sig) + if err != nil { + return fmt.Errorf("proto.Unmarshal error: %w", err) + } + + RhNym, err = s.Curve.NewG1FromBytes(sig.NymRh) + if err != nil { + return fmt.Errorf("parse rh commit: %w", err) + } + case types.AuditExpectEidNymRhNym: + var err error + RhNym, err = s.Curve.NewG1FromBytes(signature) + if err != nil { + return fmt.Errorf("parse nym commit: %w", err) + } + default: + return fmt.Errorf("invalid audit type [%d]", verType) + } + + rhAttr := bbs12381g2pub.FrFromOKM([]byte(revocationHandle)) + + nr := ipk.PKwG.H[rhIndex+1].Mul2(rhAttr, ipk.PKwG.H0, RNymRh) + + if !nr.Equals(RhNym) { + return fmt.Errorf("rh nym does not match") + } + + return nil +} diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/aries/user.go b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/user.go new file mode 100644 index 000000000..4dbc62381 --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/user.go @@ -0,0 +1,85 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package aries + +import ( + "io" + + "github.com/IBM/idemix/bccsp/types" + math "github.com/IBM/mathlib" + "github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub" + "github.com/pkg/errors" +) + +type User struct { + Curve *math.Curve + Rng io.Reader +} + +// NewKey generates a new User secret key +func (u *User) NewKey() (*math.Zr, error) { + r := u.Curve.NewRandomZr(u.Rng) + + return r, nil +} + +// NewKeyFromBytes converts the passed bytes to a User secret key +func (u *User) NewKeyFromBytes(raw []byte) (*math.Zr, error) { + if len(raw) != u.Curve.ScalarByteSize { + return nil, errors.Errorf("invalid length, expected [%d], got [%d]", u.Curve.ScalarByteSize, len(raw)) + } + + return u.Curve.NewZrFromBytes(raw), nil +} + +// MakeNym creates a new unlinkable pseudonym +func (u *User) MakeNym(sk *math.Zr, key types.IssuerPublicKey) (*math.G1, *math.Zr, error) { + ipk, ok := key.(*IssuerPublicKey) + if !ok { + return nil, nil, errors.Errorf("invalid issuer public key, expected *IssuerPublicKey, got [%T]", ipk) + } + + // Construct a commitment to the sk + // Nym = h_0^r \cdot h_1^sk + + rNym := u.Curve.NewRandomZr(u.Rng) + + cb := bbs12381g2pub.NewCommitmentBuilder(2) + cb.Add(ipk.PKwG.H0, rNym) + cb.Add(ipk.PKwG.H[UserSecretKeyIndex], sk) + nym := cb.Build() + + return nym, rNym, nil +} + +func (u *User) NewNymFromBytes(raw []byte) (*math.G1, *math.Zr, error) { + if len(raw) != u.Curve.ScalarByteSize+u.Curve.G1ByteSize { + return nil, nil, errors.Errorf("invalid length, expected [%d], got [%d]", u.Curve.ScalarByteSize+u.Curve.G1ByteSize, len(raw)) + } + + rnd := u.Curve.NewZrFromBytes(raw[:u.Curve.ScalarByteSize]) + nym, err := u.Curve.NewG1FromBytes(raw[u.Curve.ScalarByteSize:]) + if err != nil { + return nil, nil, err + } + + return nym, rnd, err +} + +// NewPublicNymFromBytes converts the passed bytes to a public nym +func (u *User) NewPublicNymFromBytes(raw []byte) (*math.G1, error) { + if len(raw) != u.Curve.G1ByteSize { + return nil, errors.Errorf("invalid length, expected [%d], got [%d]", u.Curve.G1ByteSize, len(raw)) + } + + nym, err := u.Curve.NewG1FromBytes(raw) + if err != nil { + return nil, err + } + + return nym, err +} diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/aries/util.go b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/util.go new file mode 100644 index 000000000..9021617af --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/aries/util.go @@ -0,0 +1,81 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package aries + +import ( + "github.com/IBM/idemix/bccsp/types" + math "github.com/IBM/mathlib" + "github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub" +) + +func attributesToSignatureMessage(sk *math.Zr, attributes []types.IdemixAttribute, curve *math.Curve) []*bbs12381g2pub.SignatureMessage { + var msgsZr []*bbs12381g2pub.SignatureMessage + + if sk == nil { + msgsZr = make([]*bbs12381g2pub.SignatureMessage, 0, len(attributes)) + } else { + msgsZr = make([]*bbs12381g2pub.SignatureMessage, 1, len(attributes)+1) + msgsZr[UserSecretKeyIndex] = &bbs12381g2pub.SignatureMessage{ + FR: sk, + Idx: UserSecretKeyIndex, + } + } + + for i, msg := range attributes { + switch msg.Type { + case types.IdemixBytesAttribute: + msgsZr = append(msgsZr, &bbs12381g2pub.SignatureMessage{ + FR: bbs12381g2pub.FrFromOKM(msg.Value.([]byte)), + Idx: i + 1, + }) + case types.IdemixIntAttribute: + msgsZr = append(msgsZr, &bbs12381g2pub.SignatureMessage{ + FR: curve.NewZrFromInt(int64(msg.Value.(int))), + Idx: i + 1, + }) + case types.IdemixHiddenAttribute: + continue + } + } + + return msgsZr +} + +func revealedAttributesIndex(attributes []types.IdemixAttribute) []int { + revealed := make([]int, 0, len(attributes)) + + for i, msg := range attributes { + if msg.Type != types.IdemixHiddenAttribute { + revealed = append(revealed, i+1) + } + } + + return revealed +} + +func (c *Credential) toSignatureMessage(sk *math.Zr, curve *math.Curve) []*bbs12381g2pub.SignatureMessage { + var msgsZr []*bbs12381g2pub.SignatureMessage + + if sk == nil { + msgsZr = make([]*bbs12381g2pub.SignatureMessage, 0, len(c.Attrs)) + } else { + msgsZr = make([]*bbs12381g2pub.SignatureMessage, 1, len(c.Attrs)+1) + msgsZr[UserSecretKeyIndex] = &bbs12381g2pub.SignatureMessage{ + FR: sk, + Idx: UserSecretKeyIndex, + } + } + + for i, msg := range c.Attrs { + msgsZr = append(msgsZr, &bbs12381g2pub.SignatureMessage{ + FR: curve.NewZrFromBytes(msg), + Idx: i + 1, + }) + } + + return msgsZr +} diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/credential.go b/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/credential.go new file mode 100644 index 000000000..062a1486c --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/credential.go @@ -0,0 +1,130 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package bridge + +import ( + "bytes" + + idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" + "github.com/IBM/idemix/bccsp/types" + math "github.com/IBM/mathlib" + "github.com/golang/protobuf/proto" + "github.com/pkg/errors" +) + +// Credential encapsulates the idemix algorithms to produce (sign) a credential +// and verify it. Recall that a credential is produced by the Issuer upon a credential request, +// and it is verified by the requester. +type Credential struct { + Translator idemix.Translator + Idemix *idemix.Idemix +} + +// Sign produces an idemix credential. It takes in input the issuer secret key, +// a serialised credential request, and a list of attribute values. +// Notice that attributes should not contain attributes whose type is IdemixHiddenAttribute +// cause the credential needs to carry all the attribute values. +func (c *Credential) Sign(key types.IssuerSecretKey, credentialRequest []byte, attributes []types.IdemixAttribute) (res []byte, err error) { + defer func() { + if r := recover(); r != nil { + res = nil + err = errors.Errorf("failure [%s]", r) + } + }() + + iisk, ok := key.(*IssuerSecretKey) + if !ok { + return nil, errors.Errorf("invalid issuer secret key, expected *Big, got [%T]", key) + } + + cr := &idemix.CredRequest{} + err = proto.Unmarshal(credentialRequest, cr) + if err != nil { + return nil, errors.Wrap(err, "failed unmarshalling credential request") + } + + attrValues := make([]*math.Zr, len(attributes)) + for i := 0; i < len(attributes); i++ { + switch attributes[i].Type { + case types.IdemixBytesAttribute: + attrValues[i] = c.Idemix.Curve.HashToZr(attributes[i].Value.([]byte)) + case types.IdemixIntAttribute: + var value int64 + if v, ok := attributes[i].Value.(int); ok { + value = int64(v) + } else if v, ok := attributes[i].Value.(int64); ok { + value = v + } else { + return nil, errors.Errorf("invalid int type for IdemixIntAttribute attribute") + } + attrValues[i] = c.Idemix.Curve.NewZrFromInt(value) + default: + return nil, errors.Errorf("attribute type not allowed or supported [%v] at position [%d]", attributes[i].Type, i) + } + } + + cred, err := c.Idemix.NewCredential(iisk.SK, cr, attrValues, newRandOrPanic(c.Idemix.Curve), c.Translator) + if err != nil { + return nil, errors.WithMessage(err, "failed creating new credential") + } + + return proto.Marshal(cred) +} + +// Verify checks that an idemix credential is cryptographically correct. It takes +// in input the user secret key (sk), the issuer public key (ipk), the serialised credential (credential), +// and a list of attributes. The list of attributes is optional, in case it is specified, Verify +// checks that the credential carries the specified attributes. +func (c *Credential) Verify(sk *math.Zr, ipk types.IssuerPublicKey, credential []byte, attributes []types.IdemixAttribute) (err error) { + defer func() { + if r := recover(); r != nil { + err = errors.Errorf("failure [%s]", r) + } + }() + + iipk, ok := ipk.(*IssuerPublicKey) + if !ok { + return errors.Errorf("invalid issuer public key, expected *IssuerPublicKey, got [%T]", sk) + } + + cred := &idemix.Credential{} + err = proto.Unmarshal(credential, cred) + if err != nil { + return err + } + + for i := 0; i < len(attributes); i++ { + switch attributes[i].Type { + case types.IdemixBytesAttribute: + if !bytes.Equal( + c.Idemix.Curve.HashToZr(attributes[i].Value.([]byte)).Bytes(), + cred.Attrs[i]) { + return errors.Errorf("credential does not contain the correct attribute value at position [%d]", i) + } + case types.IdemixIntAttribute: + var value int64 + if v, ok := attributes[i].Value.(int); ok { + value = int64(v) + } else if v, ok := attributes[i].Value.(int64); ok { + value = v + } else { + return errors.Errorf("invalid int type for IdemixIntAttribute attribute") + } + + if !bytes.Equal( + c.Idemix.Curve.NewZrFromInt(value).Bytes(), + cred.Attrs[i]) { + return errors.Errorf("credential does not contain the correct attribute value at position [%d]", i) + } + case types.IdemixHiddenAttribute: + continue + default: + return errors.Errorf("attribute type not allowed or supported [%v] at position [%d]", attributes[i].Type, i) + } + } + + return cred.Ver(sk, iipk.PK, c.Idemix.Curve, c.Translator) +} diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/credrequest.go b/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/credrequest.go new file mode 100644 index 000000000..8cde1593f --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/credrequest.go @@ -0,0 +1,92 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package bridge + +import ( + "bytes" + + idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" + "github.com/IBM/idemix/bccsp/types" + math "github.com/IBM/mathlib" + "github.com/golang/protobuf/proto" + "github.com/pkg/errors" +) + +// CredRequest encapsulates the idemix algorithms to produce (sign) a credential request +// and verify it. Recall that a credential request is produced by a user, +// and it is verified by the issuer at credential creation time. +type CredRequest struct { + Translator idemix.Translator + Idemix *idemix.Idemix +} + +// Sign produces an idemix credential request. It takes in input a user secret key and +// an issuer public key. +func (cr *CredRequest) Sign(sk *math.Zr, ipk types.IssuerPublicKey, nonce []byte) (res []byte, err error) { + defer func() { + if r := recover(); r != nil { + res = nil + err = errors.Errorf("failure [%s]", r) + } + }() + + iipk, ok := ipk.(*IssuerPublicKey) + if !ok { + return nil, errors.Errorf("invalid issuer public key, expected *IssuerPublicKey, got [%T]", ipk) + } + if len(nonce) != cr.Idemix.Curve.ScalarByteSize { + return nil, errors.Errorf("invalid issuer nonce, expected length %d, got %d", cr.Idemix.Curve.ScalarByteSize, len(nonce)) + } + + credRequest, err := cr.Idemix.NewCredRequest( + sk, + nonce, + iipk.PK, + newRandOrPanic(cr.Idemix.Curve), + cr.Translator, + ) + if err != nil { + return nil, err + } + + return proto.Marshal(credRequest) +} + +// Verify checks that the passed credential request is valid with the respect to the passed +// issuer public key. +func (cr *CredRequest) Verify(credentialRequest []byte, ipk types.IssuerPublicKey, nonce []byte) (err error) { + defer func() { + if r := recover(); r != nil { + err = errors.Errorf("failure [%s]", r) + } + }() + + credRequest := &idemix.CredRequest{} + err = proto.Unmarshal(credentialRequest, credRequest) + if err != nil { + return err + } + + iipk, ok := ipk.(*IssuerPublicKey) + if !ok { + return errors.Errorf("invalid issuer public key, expected *IssuerPublicKey, got [%T]", ipk) + } + + err = credRequest.Check(iipk.PK, cr.Idemix.Curve, cr.Translator) + if err != nil { + return err + } + + // Nonce checks + if len(nonce) != cr.Idemix.Curve.ScalarByteSize { + return errors.Errorf("invalid issuer nonce, expected length %d, got %d", cr.Idemix.Curve.ScalarByteSize, len(nonce)) + } + if !bytes.Equal(nonce, credRequest.IssuerNonce) { + return errors.Errorf("invalid nonce, expected [%v], got [%v]", nonce, credRequest.IssuerNonce) + } + + return nil +} diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/issuer.go b/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/issuer.go new file mode 100644 index 000000000..ae88e7270 --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/issuer.go @@ -0,0 +1,143 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package bridge + +import ( + "fmt" + + idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" + "github.com/IBM/idemix/bccsp/types" + bccsp "github.com/IBM/idemix/bccsp/types" + "github.com/golang/protobuf/proto" + "github.com/pkg/errors" +) + +// IssuerPublicKey encapsulate an idemix issuer public key. +type IssuerPublicKey struct { + PK *idemix.IssuerPublicKey +} + +func (o *IssuerPublicKey) Bytes() ([]byte, error) { + return proto.Marshal(o.PK) +} + +func (o *IssuerPublicKey) Hash() []byte { + return o.PK.Hash +} + +// IssuerPublicKey encapsulate an idemix issuer secret key. +type IssuerSecretKey struct { + SK *idemix.IssuerKey +} + +func (o *IssuerSecretKey) Bytes() ([]byte, error) { + return proto.Marshal(o.SK) +} + +func (o *IssuerSecretKey) Public() types.IssuerPublicKey { + return &IssuerPublicKey{o.SK.Ipk} +} + +// Issuer encapsulates the idemix algorithms to generate issuer key-pairs +type Issuer struct { + Translator idemix.Translator + Idemix *idemix.Idemix +} + +// NewKey generates a new issuer key-pair +func (i *Issuer) NewKey(attributeNames []string) (res types.IssuerSecretKey, err error) { + defer func() { + if r := recover(); r != nil { + res = nil + err = errors.Errorf("failure [%s]", r) + } + }() + + sk, err := i.Idemix.NewIssuerKey(attributeNames, newRandOrPanic(i.Idemix.Curve), i.Translator) + if err != nil { + return + } + + res = &IssuerSecretKey{SK: sk} + + return +} + +func (i *Issuer) NewKeyFromBytes(raw []byte, attributes []string) (res types.IssuerSecretKey, err error) { + defer func() { + if r := recover(); r != nil { + res = nil + err = errors.Errorf("failure [%s]", r) + } + }() + + sk, err := i.Idemix.NewIssuerKeyFromBytes(raw) + if err != nil { + return + } + + res = &IssuerSecretKey{SK: sk} + + return +} + +func (i *Issuer) NewPublicKeyFromBytes(raw []byte, attributes []string) (res types.IssuerPublicKey, err error) { + defer func() { + if r := recover(); r != nil { + res = nil + err = errors.Errorf("failure [%s]", r) + } + }() + + ipk := new(idemix.IssuerPublicKey) + err = proto.Unmarshal(raw, ipk) + if err != nil { + return nil, errors.WithStack(&bccsp.IdemixIssuerPublicKeyImporterError{ + Type: bccsp.IdemixIssuerPublicKeyImporterUnmarshallingError, + ErrorMsg: "failed to unmarshal issuer public key", + Cause: err}) + } + + err = ipk.SetHash(i.Idemix.Curve) + if err != nil { + return nil, errors.WithStack(&bccsp.IdemixIssuerPublicKeyImporterError{ + Type: bccsp.IdemixIssuerPublicKeyImporterHashError, + ErrorMsg: "setting the hash of the issuer public key failed", + Cause: err}) + } + + err = ipk.Check(i.Idemix.Curve, i.Translator) + if err != nil { + return nil, errors.WithStack(&bccsp.IdemixIssuerPublicKeyImporterError{ + Type: bccsp.IdemixIssuerPublicKeyImporterValidationError, + ErrorMsg: "invalid issuer public key", + Cause: err}) + } + + if len(attributes) != 0 { + // Check the attributes + if len(attributes) != len(ipk.AttributeNames) { + return nil, errors.WithStack(&bccsp.IdemixIssuerPublicKeyImporterError{ + Type: bccsp.IdemixIssuerPublicKeyImporterNumAttributesError, + ErrorMsg: fmt.Sprintf("invalid number of attributes, expected [%d], got [%d]", + len(ipk.AttributeNames), len(attributes)), + }) + } + + for i, attr := range attributes { + if ipk.AttributeNames[i] != attr { + return nil, errors.WithStack(&bccsp.IdemixIssuerPublicKeyImporterError{ + Type: bccsp.IdemixIssuerPublicKeyImporterAttributeNameError, + ErrorMsg: fmt.Sprintf("invalid attribute name at position [%d]", i), + }) + } + } + } + + res = &IssuerPublicKey{PK: ipk} + + return +} diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/nymsignaturescheme.go b/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/nymsignaturescheme.go new file mode 100644 index 000000000..c101b3102 --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/nymsignaturescheme.go @@ -0,0 +1,75 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package bridge + +import ( + idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" + "github.com/IBM/idemix/bccsp/types" + math "github.com/IBM/mathlib" + + "github.com/golang/protobuf/proto" + "github.com/pkg/errors" +) + +// NymSignatureScheme encapsulates the idemix algorithms to sign and verify using an idemix +// pseudonym. +type NymSignatureScheme struct { + Translator idemix.Translator + Idemix *idemix.Idemix +} + +// Sign produces a signature over the passed digest. It takes in input, the user secret key (sk), +// the pseudonym public key (Nym) and secret key (RNym), and the issuer public key (ipk). +func (n *NymSignatureScheme) Sign(sk *math.Zr, Nym *math.G1, RNym *math.Zr, ipk types.IssuerPublicKey, digest []byte) (res []byte, err error) { + defer func() { + if r := recover(); r != nil { + res = nil + err = errors.Errorf("failure [%s]", r) + } + }() + + iipk, ok := ipk.(*IssuerPublicKey) + if !ok { + return nil, errors.Errorf("invalid issuer public key, expected *IssuerPublicKey, got [%T]", ipk) + } + + sig, err := n.Idemix.NewNymSignature( + sk, + Nym, + RNym, + iipk.PK, + digest, + newRandOrPanic(n.Idemix.Curve), + n.Translator) + if err != nil { + return nil, errors.WithMessage(err, "failed creating new nym signature") + } + + return proto.Marshal(sig) +} + +// Verify checks that the passed signatures is valid with the respect to the passed digest, issuer public key, +// and pseudonym public key. +func (n *NymSignatureScheme) Verify(ipk types.IssuerPublicKey, Nym *math.G1, signature, digest []byte) (err error) { + defer func() { + if r := recover(); r != nil { + err = errors.Errorf("failure [%s]", r) + } + }() + + iipk, ok := ipk.(*IssuerPublicKey) + if !ok { + return errors.Errorf("invalid issuer public key, expected *IssuerPublicKey, got [%T]", ipk) + } + + sig := &idemix.NymSignature{} + err = proto.Unmarshal(signature, sig) + if err != nil { + return errors.Wrap(err, "error unmarshalling signature") + } + + return sig.Ver(Nym, iipk.PK, digest, n.Idemix.Curve, n.Translator) +} diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/rand.go b/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/rand.go new file mode 100644 index 000000000..efdd3d223 --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/rand.go @@ -0,0 +1,20 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package bridge + +import ( + "io" + + math "github.com/IBM/mathlib" +) + +func newRandOrPanic(curve *math.Curve) io.Reader { + rng, err := curve.Rand() + if err != nil { + panic(err) + } + return rng +} diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/revocation.go b/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/revocation.go new file mode 100644 index 000000000..47be5ca2b --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/revocation.go @@ -0,0 +1,76 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package bridge + +import ( + "crypto/ecdsa" + + idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" + bccsp "github.com/IBM/idemix/bccsp/types" + math "github.com/IBM/mathlib" + "github.com/golang/protobuf/proto" + "github.com/pkg/errors" +) + +// Revocation encapsulates the idemix algorithms for revocation +type Revocation struct { + Translator idemix.Translator + Idemix *idemix.Idemix +} + +// NewKey generate a new revocation key-pair. +func (r *Revocation) NewKey() (*ecdsa.PrivateKey, error) { + return r.Idemix.GenerateLongTermRevocationKey() +} + +func (r *Revocation) NewKeyFromBytes(raw []byte) (*ecdsa.PrivateKey, error) { + return r.Idemix.LongTermRevocationKeyFromBytes(raw) +} + +// Sign generates a new CRI with the respect to the passed unrevoked handles, epoch, and revocation algorithm. +func (r *Revocation) Sign(key *ecdsa.PrivateKey, unrevokedHandles [][]byte, epoch int, alg bccsp.RevocationAlgorithm) (res []byte, err error) { + defer func() { + if r := recover(); r != nil { + res = nil + err = errors.Errorf("failure [%s]", r) + } + }() + + handles := make([]*math.Zr, len(unrevokedHandles)) + for i := 0; i < len(unrevokedHandles); i++ { + handles[i] = r.Idemix.Curve.NewZrFromBytes(unrevokedHandles[i]) + } + cri, err := r.Idemix.CreateCRI(key, handles, epoch, idemix.RevocationAlgorithm(alg), newRandOrPanic(r.Idemix.Curve), r.Translator) + if err != nil { + return nil, errors.WithMessage(err, "failed creating CRI") + } + + return proto.Marshal(cri) +} + +// Verify checks that the passed serialised CRI (criRaw) is valid with the respect to the passed revocation public key, +// epoch, and revocation algorithm. +func (r *Revocation) Verify(pk *ecdsa.PublicKey, criRaw []byte, epoch int, alg bccsp.RevocationAlgorithm) (err error) { + defer func() { + if r := recover(); r != nil { + err = errors.Errorf("failure [%s]", r) + } + }() + + cri := &idemix.CredentialRevocationInformation{} + err = proto.Unmarshal(criRaw, cri) + if err != nil { + return err + } + + return r.Idemix.VerifyEpochPK( + pk, + cri.EpochPk, + cri.EpochPkSig, + int(cri.Epoch), + idemix.RevocationAlgorithm(cri.RevocationAlg), + ) +} diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/signaturescheme.go b/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/signaturescheme.go new file mode 100644 index 000000000..f50b92c6c --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/signaturescheme.go @@ -0,0 +1,274 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package bridge + +import ( + "crypto/ecdsa" + + idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" + "github.com/IBM/idemix/bccsp/types" + bccsp "github.com/IBM/idemix/bccsp/types" + math "github.com/IBM/mathlib" + "github.com/golang/protobuf/proto" + "github.com/pkg/errors" +) + +// SignatureScheme encapsulates the idemix algorithms to sign and verify using an idemix credential. +type SignatureScheme struct { + Translator idemix.Translator + Idemix *idemix.Idemix +} + +// Sign produces an idemix-signature with the respect to the passed serialised credential (cred), +// user secret key (sk), pseudonym public key (Nym) and secret key (RNym), issuer public key (ipk), +// and attributes to be disclosed. +func (s *SignatureScheme) Sign(cred []byte, sk *math.Zr, Nym *math.G1, RNym *math.Zr, ipk types.IssuerPublicKey, attributes []bccsp.IdemixAttribute, + msg []byte, rhIndex, eidIndex int, criRaw []byte, sigType bccsp.SignatureType, metadata *bccsp.IdemixSignerMetadata) (res []byte, meta *bccsp.IdemixSignerMetadata, err error) { + defer func() { + if r := recover(); r != nil { + res = nil + err = errors.Errorf("failure [%s]", r) + } + }() + + iipk, ok := ipk.(*IssuerPublicKey) + if !ok { + return nil, nil, errors.Errorf("invalid issuer public key, expected *IssuerPublicKey, got [%T]", ipk) + } + + credential := &idemix.Credential{} + err = proto.Unmarshal(cred, credential) + if err != nil { + return nil, nil, errors.Wrap(err, "failed unmarshalling credential") + } + + cri := &idemix.CredentialRevocationInformation{} + err = proto.Unmarshal(criRaw, cri) + if err != nil { + return nil, nil, errors.Wrap(err, "failed unmarshalling credential revocation information") + } + + disclosure := make([]byte, len(attributes)) + for i := 0; i < len(attributes); i++ { + if attributes[i].Type == bccsp.IdemixHiddenAttribute { + disclosure[i] = 0 + } else { + disclosure[i] = 1 + } + } + + sig, meta, err := s.Idemix.NewSignature( + credential, + sk, + Nym, + RNym, + iipk.PK, + disclosure, + msg, + rhIndex, + eidIndex, + cri, + newRandOrPanic(s.Idemix.Curve), + s.Translator, + sigType, + metadata, + ) + if err != nil { + return nil, nil, errors.WithMessage(err, "failed creating new signature") + } + + sigBytes, err := proto.Marshal(sig) + if err != nil { + return nil, nil, errors.WithMessage(err, "marshalling error") + } + + return sigBytes, meta, nil +} + +// AuditNymEid Audits the pseudonymous enrollment id of a signature +func (s *SignatureScheme) AuditNymEid( + ipk types.IssuerPublicKey, + eidIndex int, + signature []byte, + enrollmentID string, + RNymEid *math.Zr, + verType bccsp.AuditVerificationType, +) (err error) { + defer func() { + if r := recover(); r != nil { + err = errors.Errorf("failure [%s]", r) + } + }() + + iipk, ok := ipk.(*IssuerPublicKey) + if !ok { + return errors.Errorf("invalid issuer public key, expected *IssuerPublicKey, got [%T]", ipk) + } + + eidAttr := s.Idemix.Curve.HashToZr([]byte(enrollmentID)) + + switch verType { + case bccsp.AuditExpectSignature: + sig := &idemix.Signature{} + err = proto.Unmarshal(signature, sig) + if err != nil { + return err + } + return sig.AuditNymEid( + iipk.PK, + eidAttr, + eidIndex, + RNymEid, + s.Idemix.Curve, + s.Translator, + ) + case bccsp.AuditExpectEidNymRhNym: + fallthrough + case bccsp.AuditExpectEidNym: + // 1. cast signature to NymEID + nymEID := idemix.NymEID(signature) + // 2. check audit on nymEID + return nymEID.AuditNymEid( + iipk.PK, + eidAttr, + eidIndex, + RNymEid, + s.Idemix.Curve, + s.Translator, + ) + default: + return errors.Errorf("invalid audit type [%d]", verType) + } +} + +// AuditNymRh Audits the pseudonymous revocation handle of a signature +func (s *SignatureScheme) AuditNymRh( + ipk types.IssuerPublicKey, + rhIndex int, + signature []byte, + revocationHandle string, + RNymRh *math.Zr, + verType bccsp.AuditVerificationType, +) (err error) { + defer func() { + if r := recover(); r != nil { + err = errors.Errorf("failure [%s]", r) + } + }() + + iipk, ok := ipk.(*IssuerPublicKey) + if !ok { + return errors.Errorf("invalid issuer public key, expected *IssuerPublicKey, got [%T]", ipk) + } + + rhAttr := s.Idemix.Curve.HashToZr([]byte(revocationHandle)) + + switch verType { + case bccsp.AuditExpectSignature: + sig := &idemix.Signature{} + err = proto.Unmarshal(signature, sig) + if err != nil { + return err + } + return sig.AuditNymRh( + iipk.PK, + rhAttr, + rhIndex, + RNymRh, + s.Idemix.Curve, + s.Translator, + ) + case bccsp.AuditExpectEidNymRhNym: + // 1. cast signature to NymRH + nymRH := idemix.NymRH(signature) + // 2. check audit on nymRH + return nymRH.AuditNymRh( + iipk.PK, + rhAttr, + rhIndex, + RNymRh, + s.Idemix.Curve, + s.Translator, + ) + default: + return errors.Errorf("invalid audit type [%d]", verType) + } +} + +// Verify checks that an idemix signature is valid with the respect to the passed issuer public key, digest, attributes, +// revocation index (rhIndex), revocation public key, and epoch. +func (s *SignatureScheme) Verify( + ipk types.IssuerPublicKey, + signature, digest []byte, + attributes []bccsp.IdemixAttribute, + rhIndex, eidIndex int, + revocationPublicKey *ecdsa.PublicKey, + epoch int, + verType bccsp.VerificationType, + meta *bccsp.IdemixSignerMetadata, +) (err error) { + defer func() { + if r := recover(); r != nil { + err = errors.Errorf("failure [%s]", r) + } + }() + + iipk, ok := ipk.(*IssuerPublicKey) + if !ok { + return errors.Errorf("invalid issuer public key, expected *IssuerPublicKey, got [%T]", ipk) + } + + sig := &idemix.Signature{} + err = proto.Unmarshal(signature, sig) + if err != nil { + return err + } + disclosure := make([]byte, len(attributes)) + attrValues := make([]*math.Zr, len(attributes)) + for i := 0; i < len(attributes); i++ { + switch attributes[i].Type { + case bccsp.IdemixHiddenAttribute: + disclosure[i] = 0 + attrValues[i] = nil + case bccsp.IdemixBytesAttribute: + disclosure[i] = 1 + attrValues[i] = s.Idemix.Curve.HashToZr(attributes[i].Value.([]byte)) + case bccsp.IdemixIntAttribute: + var value int64 + if v, ok := attributes[i].Value.(int); ok { + value = int64(v) + } else if v, ok := attributes[i].Value.(int64); ok { + value = v + } else { + return errors.Errorf("invalid int type for IdemixIntAttribute attribute") + } + + disclosure[i] = 1 + attrValues[i] = s.Idemix.Curve.NewZrFromInt(value) + default: + err = errors.Errorf("attribute type not allowed or supported [%v] at position [%d]", attributes[i].Type, i) + } + } + if err != nil { + return + } + + return sig.Ver( + disclosure, + iipk.PK, + digest, + attrValues, + rhIndex, + eidIndex, + revocationPublicKey, + epoch, + s.Idemix.Curve, + s.Translator, + verType, + meta, + ) +} diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/user.go b/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/user.go new file mode 100644 index 000000000..bdaf20522 --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/bridge/user.go @@ -0,0 +1,96 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package bridge + +import ( + idemix "github.com/IBM/idemix/bccsp/schemes/dlog/crypto" + "github.com/IBM/idemix/bccsp/types" + math "github.com/IBM/mathlib" + "github.com/pkg/errors" +) + +// User encapsulates the idemix algorithms to generate user secret keys and pseudonym. +type User struct { + Translator idemix.Translator + Idemix *idemix.Idemix +} + +// NewKey generates an idemix user secret key +func (u *User) NewKey() (res *math.Zr, err error) { + defer func() { + if r := recover(); r != nil { + res = nil + err = errors.Errorf("failure [%s]", r) + } + }() + + res = u.Idemix.Curve.NewRandomZr(newRandOrPanic(u.Idemix.Curve)) + + return +} + +func (u *User) NewKeyFromBytes(raw []byte) (res *math.Zr, err error) { + if len(raw) != u.Idemix.Curve.ScalarByteSize { + return nil, errors.Errorf("invalid length, expected [%d], got [%d]", u.Idemix.Curve.ScalarByteSize, len(raw)) + } + + res = u.Idemix.Curve.NewZrFromBytes(raw) + + return +} + +// MakeNym generates a new pseudonym key-pair derived from the passed user secret key (sk) and issuer public key (ipk) +func (u *User) MakeNym(sk *math.Zr, ipk types.IssuerPublicKey) (r1 *math.G1, r2 *math.Zr, err error) { + defer func() { + if r := recover(); r != nil { + r1 = nil + r2 = nil + err = errors.Errorf("failure [%s]", r) + } + }() + + iipk, ok := ipk.(*IssuerPublicKey) + if !ok { + return nil, nil, errors.Errorf("invalid issuer public key, expected *IssuerPublicKey, got [%T]", ipk) + } + + ecp, big, err := u.Idemix.MakeNym(sk, iipk.PK, newRandOrPanic(u.Idemix.Curve), u.Translator) + + r1 = ecp + r2 = big + + return +} + +// MakeNym generates a new pseudonym key-pair derived from the passed user secret key (sk) and issuer public key (ipk) +func (u *User) NewNymFromBytes(raw []byte) (r1 *math.G1, r2 *math.Zr, err error) { + defer func() { + if r := recover(); r != nil { + r1 = nil + r2 = nil + err = errors.Errorf("failure [%s]", r) + } + }() + + ecp, big, err := u.Idemix.MakeNymFromBytes(raw) + r1 = ecp + r2 = big + + return +} + +func (u *User) NewPublicNymFromBytes(raw []byte) (res *math.G1, err error) { + defer func() { + if r := recover(); r != nil { + res = nil + err = errors.Errorf("failure [%s]", r) + } + }() + + res, err = u.Translator.G1FromRawBytes(raw) + + return +} diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/crypto/revocation_authority.go b/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/crypto/revocation_authority.go index a50fa3761..2b8572ca5 100644 --- a/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/crypto/revocation_authority.go +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/crypto/revocation_authority.go @@ -16,6 +16,7 @@ import ( "math/big" amcl "github.com/IBM/idemix/bccsp/schemes/dlog/crypto/translator/amcl" + weakbb "github.com/IBM/idemix/bccsp/schemes/weak-bb" math "github.com/IBM/mathlib" "github.com/golang/protobuf/proto" "github.com/pkg/errors" @@ -75,7 +76,7 @@ func createCRI(key *ecdsa.PrivateKey, unrevokedHandles []*math.Zr, epoch int, al cri.EpochPk = t.G2ToProto(curve.GenG2) } else { // create epoch key - _, epochPk := wbbKeyGen(curve, rng) + _, epochPk := weakbb.WbbKeyGen(curve, rng) cri.EpochPk = t.G2ToProto(epochPk) } diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/crypto/signature.go b/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/crypto/signature.go index 43c9ae245..53c7457ba 100644 --- a/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/crypto/signature.go +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/crypto/signature.go @@ -12,7 +12,7 @@ import ( "io" "sort" - opts "github.com/IBM/idemix/bccsp/schemes" + opts "github.com/IBM/idemix/bccsp/types" math "github.com/IBM/mathlib" "github.com/pkg/errors" ) @@ -816,7 +816,7 @@ func (sig *Signature) Ver( meta *opts.IdemixSignerMetadata, ) error { // Validate inputs - if ipk == nil || revPk == nil { + if ipk == nil { return errors.Errorf("cannot verify idemix signature: received nil input") } diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/weak-bb/LICENSE b/vendor/github.com/IBM/idemix/bccsp/schemes/weak-bb/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/weak-bb/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/crypto/weak-bb.go b/vendor/github.com/IBM/idemix/bccsp/schemes/weak-bb/weak-bb.go similarity index 70% rename from vendor/github.com/IBM/idemix/bccsp/schemes/dlog/crypto/weak-bb.go rename to vendor/github.com/IBM/idemix/bccsp/schemes/weak-bb/weak-bb.go index a228ae674..8668e4696 100644 --- a/vendor/github.com/IBM/idemix/bccsp/schemes/dlog/crypto/weak-bb.go +++ b/vendor/github.com/IBM/idemix/bccsp/schemes/weak-bb/weak-bb.go @@ -4,7 +4,7 @@ Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package idemix +package weakbb import ( "io" @@ -13,8 +13,8 @@ import ( "github.com/pkg/errors" ) -// wbbKeyGen creates a fresh weak-Boneh-Boyen signature key pair (http://ia.cr/2004/171) -func wbbKeyGen(curve *math.Curve, rng io.Reader) (*math.Zr, *math.G2) { +// WbbKeyGen creates a fresh weak-Boneh-Boyen signature key pair (http://ia.cr/2004/171) +func WbbKeyGen(curve *math.Curve, rng io.Reader) (*math.Zr, *math.G2) { // sample sk uniform from Zq sk := curve.NewRandomZr(rng) // set pk = g2^sk @@ -22,8 +22,8 @@ func wbbKeyGen(curve *math.Curve, rng io.Reader) (*math.Zr, *math.G2) { return sk, pk } -// wbbSign places a weak Boneh-Boyen signature on message m using secret key sk -func wbbSign(curve *math.Curve, sk *math.Zr, m *math.Zr) *math.G1 { +// WbbSign places a weak Boneh-Boyen signature on message m using secret key sk +func WbbSign(curve *math.Curve, sk *math.Zr, m *math.Zr) *math.G1 { // compute exp = 1/(m + sk) mod q exp := curve.ModAdd(sk, m, curve.GroupOrder) exp.InvModP(curve.GroupOrder) @@ -32,8 +32,8 @@ func wbbSign(curve *math.Curve, sk *math.Zr, m *math.Zr) *math.G1 { return curve.GenG1.Mul(exp) } -// wbbVerify verifies a weak Boneh-Boyen signature sig on message m with public key pk -func wbbVerify(curve *math.Curve, pk *math.G2, sig *math.G1, m *math.Zr) error { +// WbbVerify verifies a weak Boneh-Boyen signature sig on message m with public key pk +func WbbVerify(curve *math.Curve, pk *math.G2, sig *math.G1, m *math.Zr) error { if pk == nil || sig == nil || m == nil { return errors.Errorf("Weak-BB signature invalid: received nil input") } diff --git a/vendor/github.com/IBM/idemix/bccsp/types/LICENSE b/vendor/github.com/IBM/idemix/bccsp/types/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/types/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/crypto.go b/vendor/github.com/IBM/idemix/bccsp/types/crypto.go similarity index 99% rename from vendor/github.com/IBM/idemix/bccsp/schemes/crypto.go rename to vendor/github.com/IBM/idemix/bccsp/types/crypto.go index e4cfa52a3..aa3dffd02 100644 --- a/vendor/github.com/IBM/idemix/bccsp/schemes/crypto.go +++ b/vendor/github.com/IBM/idemix/bccsp/types/crypto.go @@ -3,7 +3,7 @@ Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package idemix +package types import ( "crypto" diff --git a/vendor/github.com/IBM/idemix/bccsp/types/idemix.go b/vendor/github.com/IBM/idemix/bccsp/types/idemix.go new file mode 100644 index 000000000..a44c74a92 --- /dev/null +++ b/vendor/github.com/IBM/idemix/bccsp/types/idemix.go @@ -0,0 +1,179 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package types + +import ( + "crypto/ecdsa" + + math "github.com/IBM/mathlib" +) + +// IssuerPublicKey is the issuer public key +type IssuerPublicKey interface { + + // Bytes returns the byte representation of this key + Bytes() ([]byte, error) + + // Hash returns the hash representation of this key. + // The output is supposed to be collision-resistant + Hash() []byte +} + +// IssuerPublicKey is the issuer secret key +type IssuerSecretKey interface { + + // Bytes returns the byte representation of this key + Bytes() ([]byte, error) + + // Public returns the corresponding public key + Public() IssuerPublicKey +} + +// Issuer is a local interface to decouple from the idemix implementation +type Issuer interface { + // NewKey generates a new idemix issuer key w.r.t the passed attribute names. + NewKey(AttributeNames []string) (IssuerSecretKey, error) + + // NewPublicKeyFromBytes converts the passed bytes to an Issuer key + // It makes sure that the so obtained key has the passed attributes, if specified + NewKeyFromBytes(raw []byte, attributes []string) (IssuerSecretKey, error) + + // NewPublicKeyFromBytes converts the passed bytes to an Issuer public key + // It makes sure that the so obtained public key has the passed attributes, if specified + NewPublicKeyFromBytes(raw []byte, attributes []string) (IssuerPublicKey, error) +} + +// User is a local interface to decouple from the idemix implementation +type User interface { + // NewKey generates a new User secret key + NewKey() (*math.Zr, error) + + // NewKeyFromBytes converts the passed bytes to a User secret key + NewKeyFromBytes(raw []byte) (*math.Zr, error) + + // MakeNym creates a new unlinkable pseudonym + MakeNym(sk *math.Zr, key IssuerPublicKey) (*math.G1, *math.Zr, error) + + NewNymFromBytes(raw []byte) (*math.G1, *math.Zr, error) + + // NewPublicNymFromBytes converts the passed bytes to a public nym + NewPublicNymFromBytes(raw []byte) (*math.G1, error) +} + +// CredRequest is a local interface to decouple from the idemix implementation +// of the issuance of credential requests. +type CredRequest interface { + // Sign creates a new Credential Request, the first message of the interactive credential issuance protocol + // (from user to issuer) + Sign(sk *math.Zr, ipk IssuerPublicKey, nonce []byte) ([]byte, error) + + // Verify verifies the credential request + Verify(credRequest []byte, ipk IssuerPublicKey, nonce []byte) error +} + +// BlindCredRequest is similar to CredRequest except it provides a hiding +// commitment to the hidden attribute (the user secret key), and so it +// requires an additional `Unblind` function to account for the randomness +type BlindCredRequest interface { + // Blind creates a request to sign the value sk + Blind(sk *math.Zr, ipk IssuerPublicKey, nonce []byte) ([]byte, []byte, error) + + // Verify verifies the credential request + BlindVerify(credRequest []byte, ipk IssuerPublicKey, nonce []byte) error + + // Unblind takes a blinded signature and a blinding and produces a standard signature + Unblind(signature, blinding []byte) ([]byte, error) +} + +// CredRequest is a local interface to decouple from the idemix implementation +// of the issuance of credentials. +type Credential interface { + + // Sign issues a new credential, which is the last step of the interactive issuance protocol + // All attribute values are added by the issuer at this step and then signed together with a commitment to + // the user's secret key from a credential request + Sign(key IssuerSecretKey, credentialRequest []byte, attributes []IdemixAttribute) ([]byte, error) + + // Verify cryptographically verifies the credential by verifying the signature + // on the attribute values and user's secret key + Verify(sk *math.Zr, ipk IssuerPublicKey, credential []byte, attributes []IdemixAttribute) error +} + +// Revocation is a local interface to decouple from the idemix implementation +// the revocation-related operations +type Revocation interface { + + // NewKey generates a long term signing key that will be used for revocation + NewKey() (*ecdsa.PrivateKey, error) + + // NewKeyFromBytes generates a long term signing key that will be used for revocation from the passed bytes + NewKeyFromBytes(raw []byte) (*ecdsa.PrivateKey, error) + + // Sign creates the Credential Revocation Information for a certain time period (epoch). + // Users can use the CRI to prove that they are not revoked. + // Note that when not using revocation (i.e., alg = ALG_NO_REVOCATION), the entered unrevokedHandles are not used, + // and the resulting CRI can be used by any signer. + Sign(key *ecdsa.PrivateKey, unrevokedHandles [][]byte, epoch int, alg RevocationAlgorithm) ([]byte, error) + + // Verify verifies that the revocation PK for a certain epoch is valid, + // by checking that it was signed with the long term revocation key. + // Note that even if we use no revocation (i.e., alg = ALG_NO_REVOCATION), we need + // to verify the signature to make sure the issuer indeed signed that no revocation + // is used in this epoch. + Verify(pk *ecdsa.PublicKey, cri []byte, epoch int, alg RevocationAlgorithm) error +} + +// SignatureScheme is a local interface to decouple from the idemix implementation +// the sign-related operations +type SignatureScheme interface { + // Sign creates a new idemix signature (Schnorr-type signature). + // The attributes slice steers which attributes are disclosed: + // If attributes[i].Type == bccsp.IdemixHiddenAttribute then attribute i remains hidden and otherwise it is disclosed. + // We require the revocation handle to remain undisclosed (i.e., attributes[rhIndex] == bccsp.IdemixHiddenAttribute). + // Parameters are to be understood as follow: + // cred: the serialized version of an idemix credential; + // sk: the user secret key; + // (Nym, RNym): Nym key-pair; + // ipk: issuer public key; + // attributes: as described above; + // msg: the message to be signed; + // rhIndex: revocation handle index relative to attributes; + // cri: the serialized version of the Credential Revocation Information (it contains the epoch this signature + // is created in reference to). + Sign(cred []byte, sk *math.Zr, Nym *math.G1, RNym *math.Zr, ipk IssuerPublicKey, attributes []IdemixAttribute, msg []byte, rhIndex, eidIndex int, cri []byte, sigType SignatureType, metadata *IdemixSignerMetadata) ([]byte, *IdemixSignerMetadata, error) + + // Verify verifies an idemix signature. + // The attribute slice steers which attributes it expects to be disclosed + // If attributes[i].Type == bccsp.IdemixHiddenAttribute then attribute i remains hidden and otherwise + // attributes[i].Value is expected to contain the disclosed attribute value. + // In other words, this function will check that if attribute i is disclosed, the i-th attribute equals attributes[i].Value. + // Parameters are to be understood as follow: + // ipk: issuer public key; + // signature: signature to verify; + // msg: message signed; + // attributes: as described above; + // rhIndex: revocation handle index relative to attributes; + // revocationPublicKey: revocation public key; + // epoch: revocation epoch. + Verify(ipk IssuerPublicKey, signature, msg []byte, attributes []IdemixAttribute, rhIndex, eidIndex int, + revocationPublicKey *ecdsa.PublicKey, epoch int, verType VerificationType, meta *IdemixSignerMetadata) error + + // AuditNymEid permits the auditing of the nym eid generated by a signer + AuditNymEid(ipk IssuerPublicKey, eidIndex int, signature []byte, enrollmentID string, RNymEid *math.Zr, verType AuditVerificationType) error + + // AuditNymRh permits the auditing of the nym rh generated by a signer + AuditNymRh(ipk IssuerPublicKey, rhIndex int, signature []byte, revocationHandle string, RNymRh *math.Zr, verType AuditVerificationType) error +} + +// NymSignatureScheme is a local interface to decouple from the idemix implementation +// the nym sign-related operations +type NymSignatureScheme interface { + // Sign creates a new idemix pseudonym signature + Sign(sk *math.Zr, Nym *math.G1, RNym *math.Zr, ipk IssuerPublicKey, digest []byte) ([]byte, error) + + // Verify verifies an idemix NymSignature + Verify(pk IssuerPublicKey, Nym *math.G1, signature, digest []byte) error +} diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/idemixerrs.go b/vendor/github.com/IBM/idemix/bccsp/types/idemixerrs.go similarity index 98% rename from vendor/github.com/IBM/idemix/bccsp/schemes/idemixerrs.go rename to vendor/github.com/IBM/idemix/bccsp/types/idemixerrs.go index 99c620e18..d0d3f64fc 100644 --- a/vendor/github.com/IBM/idemix/bccsp/schemes/idemixerrs.go +++ b/vendor/github.com/IBM/idemix/bccsp/types/idemixerrs.go @@ -4,7 +4,7 @@ Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package idemix +package types import ( "fmt" diff --git a/vendor/github.com/IBM/idemix/bccsp/schemes/idemixopts.go b/vendor/github.com/IBM/idemix/bccsp/types/idemixopts.go similarity index 94% rename from vendor/github.com/IBM/idemix/bccsp/schemes/idemixopts.go rename to vendor/github.com/IBM/idemix/bccsp/types/idemixopts.go index 9f18cf80c..393f3262e 100644 --- a/vendor/github.com/IBM/idemix/bccsp/schemes/idemixopts.go +++ b/vendor/github.com/IBM/idemix/bccsp/types/idemixopts.go @@ -3,7 +3,7 @@ Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package idemix +package types import ( "crypto" @@ -190,6 +190,27 @@ func (o *IdemixCredentialRequestSignerOpts) HashFunc() crypto.Hash { return o.H } +// IdemixCredentialRequestSignerOpts contains the option to create a Idemix credential request. +type IdemixBlindCredentialRequestSignerOpts struct { + // Attributes contains a list of indices of the attributes to be included in the + // credential. The indices are with the respect to IdemixIssuerKeyGenOpts#AttributeNames. + Attributes []int + // IssuerPK is the public-key of the issuer + IssuerPK Key + // IssuerNonce is generated by the issuer and used by the client to generate the credential request. + // Once the issuer gets the credential requests, it checks that the nonce is the same. + IssuerNonce []byte + // HashFun is the hash function to be used + H crypto.Hash + + // Blinding contains the blinding used to mask the unrevealed attributes + Blinding []byte +} + +func (o *IdemixBlindCredentialRequestSignerOpts) HashFunc() crypto.Hash { + return o.H +} + // IssuerPublicKey returns the issuer public key used to derive // a new unlinkable pseudonym from a credential secret key func (o *IdemixCredentialRequestSignerOpts) IssuerPublicKey() Key { diff --git a/vendor/github.com/IBM/idemix/common/flogging/core.go b/vendor/github.com/IBM/idemix/common/flogging/core.go new file mode 100644 index 000000000..2e0317351 --- /dev/null +++ b/vendor/github.com/IBM/idemix/common/flogging/core.go @@ -0,0 +1,125 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package flogging + +import ( + "go.uber.org/zap/zapcore" +) + +type Encoding int8 + +const ( + CONSOLE = iota + JSON + LOGFMT +) + +// EncodingSelector is used to determine whether log records are +// encoded as JSON or in human readable CONSOLE or LOGFMT formats. +type EncodingSelector interface { + Encoding() Encoding +} + +// Core is a custom implementation of a zapcore.Core. It's a terrible hack that +// only exists to work around the intersection of state associated with +// encoders, implementation hiding in zapcore, and implicit, ad-hoc logger +// initialization within fabric. +// +// In addition to encoding log entries and fields to a buffer, zap Encoder +// implementations also need to maintain field state. When zapcore.Core.With is +// used, the associated encoder is cloned and the fields are added to the +// encoder. This means that encoder instances cannot be shared across cores. +// +// In terms of implementation hiding, it's difficult for our FormatEncoder to +// cleanly wrap the JSON and console implementations from zap as all methods +// from the zapcore.ObjectEncoder would need to be implemented to delegate to +// the correct backend. +// +// This implementation works by associating multiple encoders with a core. When +// fields are added to the core, the fields are added to all of the encoder +// implementations. The core also references the logging configuration to +// determine the proper encoding to use, the writer to delegate to, and the +// enabled levels. +type Core struct { + zapcore.LevelEnabler + Levels *LoggerLevels + Encoders map[Encoding]zapcore.Encoder + Selector EncodingSelector + Output zapcore.WriteSyncer + Observer Observer +} + +//go:generate counterfeiter -o mock/observer.go -fake-name Observer . Observer + +type Observer interface { + Check(e zapcore.Entry, ce *zapcore.CheckedEntry) + WriteEntry(e zapcore.Entry, fields []zapcore.Field) +} + +func (c *Core) With(fields []zapcore.Field) zapcore.Core { + clones := map[Encoding]zapcore.Encoder{} + for name, enc := range c.Encoders { + clone := enc.Clone() + addFields(clone, fields) + clones[name] = clone + } + + return &Core{ + LevelEnabler: c.LevelEnabler, + Levels: c.Levels, + Encoders: clones, + Selector: c.Selector, + Output: c.Output, + Observer: c.Observer, + } +} + +func (c *Core) Check(e zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry { + if c.Observer != nil { + c.Observer.Check(e, ce) + } + + if c.Enabled(e.Level) && c.Levels.Level(e.LoggerName).Enabled(e.Level) { + return ce.AddCore(e, c) + } + return ce +} + +func (c *Core) Write(e zapcore.Entry, fields []zapcore.Field) error { + encoding := c.Selector.Encoding() + enc := c.Encoders[encoding] + + buf, err := enc.EncodeEntry(e, fields) + if err != nil { + return err + } + _, err = c.Output.Write(buf.Bytes()) + buf.Free() + if err != nil { + return err + } + + if e.Level >= zapcore.PanicLevel { + c.Sync() + } + + if c.Observer != nil { + c.Observer.WriteEntry(e, fields) + } + + return nil +} + +func (c *Core) Sync() error { + return c.Output.Sync() +} + +func addFields(enc zapcore.ObjectEncoder, fields []zapcore.Field) { + for i := range fields { + fields[i].AddTo(enc) + } +} diff --git a/vendor/github.com/IBM/idemix/common/flogging/fabenc/color.go b/vendor/github.com/IBM/idemix/common/flogging/fabenc/color.go new file mode 100644 index 000000000..240d0f71c --- /dev/null +++ b/vendor/github.com/IBM/idemix/common/flogging/fabenc/color.go @@ -0,0 +1,39 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package fabenc + +import ( + "fmt" +) + +type Color uint8 + +const ColorNone Color = 0 + +const ( + ColorBlack Color = iota + 30 + ColorRed + ColorGreen + ColorYellow + ColorBlue + ColorMagenta + ColorCyan + ColorWhite +) + +func (c Color) Normal() string { + return fmt.Sprintf("\x1b[%dm", c) +} + +func (c Color) Bold() string { + if c == ColorNone { + return c.Normal() + } + return fmt.Sprintf("\x1b[%d;1m", c) +} + +func ResetColor() string { return ColorNone.Normal() } diff --git a/vendor/github.com/IBM/idemix/common/flogging/fabenc/encoder.go b/vendor/github.com/IBM/idemix/common/flogging/fabenc/encoder.go new file mode 100644 index 000000000..4d19a098e --- /dev/null +++ b/vendor/github.com/IBM/idemix/common/flogging/fabenc/encoder.go @@ -0,0 +1,80 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package fabenc + +import ( + "io" + "time" + + zaplogfmt "github.com/sykesm/zap-logfmt" + "go.uber.org/zap/buffer" + "go.uber.org/zap/zapcore" +) + +// A FormatEncoder is a zapcore.Encoder that formats log records according to a +// go-logging based format specifier. +type FormatEncoder struct { + zapcore.Encoder + formatters []Formatter + pool buffer.Pool +} + +// A Formatter is used to format and write data from a zap log entry. +type Formatter interface { + Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) +} + +func NewFormatEncoder(formatters ...Formatter) *FormatEncoder { + return &FormatEncoder{ + Encoder: zaplogfmt.NewEncoder(zapcore.EncoderConfig{ + MessageKey: "", // disable + LevelKey: "", // disable + TimeKey: "", // disable + NameKey: "", // disable + CallerKey: "", // disable + StacktraceKey: "", // disable + LineEnding: "\n", + EncodeDuration: zapcore.StringDurationEncoder, + EncodeTime: func(t time.Time, enc zapcore.PrimitiveArrayEncoder) { + enc.AppendString(t.Format("2006-01-02T15:04:05.999Z07:00")) + }, + }), + formatters: formatters, + pool: buffer.NewPool(), + } +} + +// Clone creates a new instance of this encoder with the same configuration. +func (f *FormatEncoder) Clone() zapcore.Encoder { + return &FormatEncoder{ + Encoder: f.Encoder.Clone(), + formatters: f.formatters, + pool: f.pool, + } +} + +// EncodeEntry formats a zap log record. The structured fields are formatted by a +// zapcore.ConsoleEncoder and are appended as JSON to the end of the formatted entry. +// All entries are terminated by a newline. +func (f *FormatEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) { + line := f.pool.Get() + for _, f := range f.formatters { + f.Format(line, entry, fields) + } + + encodedFields, err := f.Encoder.EncodeEntry(entry, fields) + if err != nil { + return nil, err + } + if line.Len() > 0 && encodedFields.Len() != 1 { + line.AppendString(" ") + } + line.AppendString(encodedFields.String()) + encodedFields.Free() + + return line, nil +} diff --git a/vendor/github.com/IBM/idemix/common/flogging/fabenc/formatter.go b/vendor/github.com/IBM/idemix/common/flogging/fabenc/formatter.go new file mode 100644 index 000000000..b2101e5db --- /dev/null +++ b/vendor/github.com/IBM/idemix/common/flogging/fabenc/formatter.go @@ -0,0 +1,294 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package fabenc + +import ( + "fmt" + "io" + "regexp" + "runtime" + "strings" + "sync" + "sync/atomic" + + "go.uber.org/zap/zapcore" +) + +// formatRegexp is broken into three groups: +// 1. the format verb +// 2. an optional colon that is ungrouped with '?:' +// 3. an optional, non-greedy format directive +// +// The grouping simplifies the verb proccssing during spec parsing. +var formatRegexp = regexp.MustCompile(`%{(color|id|level|message|module|shortfunc|time)(?::(.*?))?}`) + +// ParseFormat parses a log format spec and returns a slice of formatters +// that should be iterated over to build a formatted log record. +// +// The op-loggng specifiers supported by this formatter are: +// - %{color} - level specific SGR color escape or SGR reset +// - %{id} - a unique log sequence number +// - %{level} - the log level of the entry +// - %{message} - the log message +// - %{module} - the zap logger name +// - %{shortfunc} - the name of the function creating the log record +// - %{time} - the time the log entry was created +// +// Specifiers may include an optional format verb: +// - color: reset|bold +// - id: a fmt style numeric formatter without the leading % +// - level: a fmt style string formatter without the leading % +// - message: a fmt style string formatter without the leading % +// - module: a fmt style string formatter without the leading % +func ParseFormat(spec string) ([]Formatter, error) { + cursor := 0 + formatters := []Formatter{} + + // iterate over the regex groups and convert to formatters + matches := formatRegexp.FindAllStringSubmatchIndex(spec, -1) + for _, m := range matches { + start, end := m[0], m[1] + verbStart, verbEnd := m[2], m[3] + formatStart, formatEnd := m[4], m[5] + + if start > cursor { + formatters = append(formatters, StringFormatter{Value: spec[cursor:start]}) + } + + var format string + if formatStart >= 0 { + format = spec[formatStart:formatEnd] + } + + formatter, err := NewFormatter(spec[verbStart:verbEnd], format) + if err != nil { + return nil, err + } + + formatters = append(formatters, formatter) + cursor = end + } + + // handle any trailing suffix + if cursor != len(spec) { + formatters = append(formatters, StringFormatter{Value: spec[cursor:]}) + } + + return formatters, nil +} + +// A MultiFormatter presents multiple formatters as a single Formatter. It can +// be used to change the set of formatters associated with an encoder at +// runtime. +type MultiFormatter struct { + mutex sync.RWMutex + formatters []Formatter +} + +// NewMultiFormatter creates a new MultiFormatter that delegates to the +// provided formatters. The formatters are used in the order they are +// presented. +func NewMultiFormatter(formatters ...Formatter) *MultiFormatter { + return &MultiFormatter{ + formatters: formatters, + } +} + +// Format iterates over its delegates to format a log record to the provided +// buffer. +func (m *MultiFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) { + m.mutex.RLock() + for i := range m.formatters { + m.formatters[i].Format(w, entry, fields) + } + m.mutex.RUnlock() +} + +// SetFormatters replaces the delegate formatters. +func (m *MultiFormatter) SetFormatters(formatters []Formatter) { + m.mutex.Lock() + m.formatters = formatters + m.mutex.Unlock() +} + +// A StringFormatter formats a fixed string. +type StringFormatter struct{ Value string } + +// Format writes the formatter's fixed string to provided writer. +func (s StringFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) { + fmt.Fprintf(w, "%s", s.Value) +} + +// NewFormatter creates the formatter for the provided verb. When a format is +// not provided, the default format for the verb is used. +func NewFormatter(verb, format string) (Formatter, error) { + switch verb { + case "color": + return newColorFormatter(format) + case "id": + return newSequenceFormatter(format), nil + case "level": + return newLevelFormatter(format), nil + case "message": + return newMessageFormatter(format), nil + case "module": + return newModuleFormatter(format), nil + case "shortfunc": + return newShortFuncFormatter(format), nil + case "time": + return newTimeFormatter(format), nil + default: + return nil, fmt.Errorf("unknown verb: %s", verb) + } +} + +// A ColorFormatter formats an SGR color code. +type ColorFormatter struct { + Bold bool // set the bold attribute + Reset bool // reset colors and attributes +} + +func newColorFormatter(f string) (ColorFormatter, error) { + switch f { + case "bold": + return ColorFormatter{Bold: true}, nil + case "reset": + return ColorFormatter{Reset: true}, nil + case "": + return ColorFormatter{}, nil + default: + return ColorFormatter{}, fmt.Errorf("invalid color option: %s", f) + } +} + +// LevelColor returns the Color associated with a specific zap logging level. +func (c ColorFormatter) LevelColor(l zapcore.Level) Color { + switch l { + case zapcore.DebugLevel: + return ColorCyan + case zapcore.InfoLevel: + return ColorBlue + case zapcore.WarnLevel: + return ColorYellow + case zapcore.ErrorLevel: + return ColorRed + case zapcore.DPanicLevel, zapcore.PanicLevel: + return ColorMagenta + case zapcore.FatalLevel: + return ColorMagenta + default: + return ColorNone + } +} + +// Format writes the SGR color code to the provided writer. +func (c ColorFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) { + switch { + case c.Reset: + fmt.Fprint(w, ResetColor()) + case c.Bold: + fmt.Fprint(w, c.LevelColor(entry.Level).Bold()) + default: + fmt.Fprint(w, c.LevelColor(entry.Level).Normal()) + } +} + +// LevelFormatter formats a log level. +type LevelFormatter struct{ FormatVerb string } + +func newLevelFormatter(f string) LevelFormatter { + return LevelFormatter{FormatVerb: "%" + stringOrDefault(f, "s")} +} + +// Format writes the logging level to the provided writer. +func (l LevelFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) { + fmt.Fprintf(w, l.FormatVerb, entry.Level.CapitalString()) +} + +// MessageFormatter formats a log message. +type MessageFormatter struct{ FormatVerb string } + +func newMessageFormatter(f string) MessageFormatter { + return MessageFormatter{FormatVerb: "%" + stringOrDefault(f, "s")} +} + +// Format writes the log entry message to the provided writer. +func (m MessageFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) { + fmt.Fprintf(w, m.FormatVerb, strings.TrimRight(entry.Message, "\n")) +} + +// ModuleFormatter formats the zap logger name. +type ModuleFormatter struct{ FormatVerb string } + +func newModuleFormatter(f string) ModuleFormatter { + return ModuleFormatter{FormatVerb: "%" + stringOrDefault(f, "s")} +} + +// Format writes the zap logger name to the specified writer. +func (m ModuleFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) { + fmt.Fprintf(w, m.FormatVerb, entry.LoggerName) +} + +// sequence maintains the global sequence number shared by all SequeneFormatter +// instances. +var sequence uint64 + +// SetSequence explicitly sets the global sequence number. +func SetSequence(s uint64) { atomic.StoreUint64(&sequence, s) } + +// SequenceFormatter formats a global sequence number. +type SequenceFormatter struct{ FormatVerb string } + +func newSequenceFormatter(f string) SequenceFormatter { + return SequenceFormatter{FormatVerb: "%" + stringOrDefault(f, "d")} +} + +// SequenceFormatter increments a global sequence number and writes it to the +// provided writer. +func (s SequenceFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) { + fmt.Fprintf(w, s.FormatVerb, atomic.AddUint64(&sequence, 1)) +} + +// ShortFuncFormatter formats the name of the function creating the log record. +type ShortFuncFormatter struct{ FormatVerb string } + +func newShortFuncFormatter(f string) ShortFuncFormatter { + return ShortFuncFormatter{FormatVerb: "%" + stringOrDefault(f, "s")} +} + +// Format writes the calling function name to the provided writer. The name is obtained from +// the runtime and the package and line numbers are discarded. +func (s ShortFuncFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) { + f := runtime.FuncForPC(entry.Caller.PC) + if f == nil { + fmt.Fprintf(w, s.FormatVerb, "(unknown)") + return + } + + fname := f.Name() + funcIdx := strings.LastIndex(fname, ".") + fmt.Fprintf(w, s.FormatVerb, fname[funcIdx+1:]) +} + +// TimeFormatter formats the time from the zap log entry. +type TimeFormatter struct{ Layout string } + +func newTimeFormatter(f string) TimeFormatter { + return TimeFormatter{Layout: stringOrDefault(f, "2006-01-02T15:04:05.999Z07:00")} +} + +// Format writes the log record time stamp to the provided writer. +func (t TimeFormatter) Format(w io.Writer, entry zapcore.Entry, fields []zapcore.Field) { + fmt.Fprint(w, entry.Time.Format(t.Layout)) +} + +func stringOrDefault(str, dflt string) string { + if str != "" { + return str + } + return dflt +} diff --git a/vendor/github.com/IBM/idemix/common/flogging/global.go b/vendor/github.com/IBM/idemix/common/flogging/global.go new file mode 100644 index 000000000..7db1588bf --- /dev/null +++ b/vendor/github.com/IBM/idemix/common/flogging/global.go @@ -0,0 +1,84 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package flogging + +import ( + "io" + + "go.uber.org/zap/zapcore" + "google.golang.org/grpc/grpclog" +) + +const ( + defaultFormat = "%{color}%{time:2006-01-02 15:04:05.000 MST} [%{module}] %{shortfunc} -> %{level:.4s} %{id:03x}%{color:reset} %{message}" + defaultLevel = zapcore.InfoLevel +) + +var Global *Logging + +func init() { + logging, err := New(Config{}) + if err != nil { + panic(err) + } + + Global = logging + grpcLogger := Global.ZapLogger("grpc") + grpclog.SetLogger(NewGRPCLogger(grpcLogger)) +} + +// Init initializes logging with the provided config. +func Init(config Config) { + err := Global.Apply(config) + if err != nil { + panic(err) + } +} + +// Reset sets logging to the defaults defined in this package. +// +// Used in tests and in the package init +func Reset() { + Global.Apply(Config{}) +} + +// LoggerLevel gets the current logging level for the logger with the +// provided name. +func LoggerLevel(loggerName string) string { + return Global.Level(loggerName).String() +} + +// MustGetLogger creates a logger with the specified name. If an invalid name +// is provided, the operation will panic. +func MustGetLogger(loggerName string) *FabricLogger { + return Global.Logger(loggerName) +} + +// ActivateSpec is used to activate a logging specification. +func ActivateSpec(spec string) { + err := Global.ActivateSpec(spec) + if err != nil { + panic(err) + } +} + +// DefaultLevel returns the default log level. +func DefaultLevel() string { + return defaultLevel.String() +} + +// SetWriter calls SetWriter returning the previous value +// of the writer. +func SetWriter(w io.Writer) io.Writer { + return Global.SetWriter(w) +} + +// SetObserver calls SetObserver returning the previous value +// of the observer. +func SetObserver(observer Observer) Observer { + return Global.SetObserver(observer) +} diff --git a/vendor/github.com/IBM/idemix/common/flogging/levels.go b/vendor/github.com/IBM/idemix/common/flogging/levels.go new file mode 100644 index 000000000..dae518def --- /dev/null +++ b/vendor/github.com/IBM/idemix/common/flogging/levels.go @@ -0,0 +1,68 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package flogging + +import ( + "fmt" + "math" + + "go.uber.org/zap/zapcore" +) + +const ( + // DisabledLevel represents a disabled log level. Logs at this level should + // never be emitted. + DisabledLevel = zapcore.Level(math.MinInt8) + + // PayloadLevel is used to log the extremely detailed message level debug + // information. + PayloadLevel = zapcore.Level(zapcore.DebugLevel - 1) +) + +// NameToLevel converts a level name to a zapcore.Level. If the level name is +// unknown, zapcore.InfoLevel is returned. +func NameToLevel(level string) zapcore.Level { + l, err := nameToLevel(level) + if err != nil { + return zapcore.InfoLevel + } + return l +} + +func nameToLevel(level string) (zapcore.Level, error) { + switch level { + case "PAYLOAD", "payload": + return PayloadLevel, nil + case "DEBUG", "debug": + return zapcore.DebugLevel, nil + case "INFO", "info": + return zapcore.InfoLevel, nil + case "WARNING", "WARN", "warning", "warn": + return zapcore.WarnLevel, nil + case "ERROR", "error": + return zapcore.ErrorLevel, nil + case "DPANIC", "dpanic": + return zapcore.DPanicLevel, nil + case "PANIC", "panic": + return zapcore.PanicLevel, nil + case "FATAL", "fatal": + return zapcore.FatalLevel, nil + + case "NOTICE", "notice": + return zapcore.InfoLevel, nil // future + case "CRITICAL", "critical": + return zapcore.ErrorLevel, nil // future + + default: + return DisabledLevel, fmt.Errorf("invalid log level: %s", level) + } +} + +func IsValidLevel(level string) bool { + _, err := nameToLevel(level) + return err == nil +} diff --git a/vendor/github.com/IBM/idemix/common/flogging/loggerlevels.go b/vendor/github.com/IBM/idemix/common/flogging/loggerlevels.go new file mode 100644 index 000000000..d306e624d --- /dev/null +++ b/vendor/github.com/IBM/idemix/common/flogging/loggerlevels.go @@ -0,0 +1,175 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package flogging + +import ( + "fmt" + "regexp" + "sort" + "strings" + "sync" + + "github.com/pkg/errors" + "go.uber.org/zap/zapcore" +) + +// LoggerLevels tracks the logging level of named loggers. +type LoggerLevels struct { + mutex sync.RWMutex + levelCache map[string]zapcore.Level + specs map[string]zapcore.Level + defaultLevel zapcore.Level + minLevel zapcore.Level +} + +// DefaultLevel returns the default logging level for loggers that do not have +// an explicit level set. +func (l *LoggerLevels) DefaultLevel() zapcore.Level { + l.mutex.RLock() + lvl := l.defaultLevel + l.mutex.RUnlock() + return lvl +} + +// ActivateSpec is used to modify logging levels. +// +// The logging specification has the following form: +// +// [[,...]=][:[[,...]=]...] +func (l *LoggerLevels) ActivateSpec(spec string) error { + l.mutex.Lock() + defer l.mutex.Unlock() + + defaultLevel := zapcore.InfoLevel + specs := map[string]zapcore.Level{} + for _, field := range strings.Split(spec, ":") { + split := strings.Split(field, "=") + switch len(split) { + case 1: // level + if field != "" && !IsValidLevel(field) { + return errors.Errorf("invalid logging specification '%s': bad segment '%s'", spec, field) + } + defaultLevel = NameToLevel(field) + + case 2: // [,...]= + if split[0] == "" { + return errors.Errorf("invalid logging specification '%s': no logger specified in segment '%s'", spec, field) + } + if field != "" && !IsValidLevel(split[1]) { + return errors.Errorf("invalid logging specification '%s': bad segment '%s'", spec, field) + } + + level := NameToLevel(split[1]) + loggers := strings.Split(split[0], ",") + for _, logger := range loggers { + // check if the logger name in the spec is valid. The + // trailing period is trimmed as logger names in specs + // ending with a period signifies that this part of the + // spec refers to the exact logger name (i.e. is not a prefix) + if !isValidLoggerName(strings.TrimSuffix(logger, ".")) { + return errors.Errorf("invalid logging specification '%s': bad logger name '%s'", spec, logger) + } + specs[logger] = level + } + + default: + return errors.Errorf("invalid logging specification '%s': bad segment '%s'", spec, field) + } + } + + minLevel := defaultLevel + for _, lvl := range specs { + if lvl < minLevel { + minLevel = lvl + } + } + + l.minLevel = minLevel + l.defaultLevel = defaultLevel + l.specs = specs + l.levelCache = map[string]zapcore.Level{} + + return nil +} + +// logggerNameRegexp defines the valid logger names +var loggerNameRegexp = regexp.MustCompile(`^[[:alnum:]_#:-]+(\.[[:alnum:]_#:-]+)*$`) + +// isValidLoggerName checks whether a logger name contains only valid +// characters. Names that begin/end with periods or contain special +// characters (other than periods, underscores, pound signs, colons +// and dashes) are invalid. +func isValidLoggerName(loggerName string) bool { + return loggerNameRegexp.MatchString(loggerName) +} + +// Level returns the effective logging level for a logger. If a level has not +// been explicitly set for the logger, the default logging level will be +// returned. +func (l *LoggerLevels) Level(loggerName string) zapcore.Level { + if level, ok := l.cachedLevel(loggerName); ok { + return level + } + + l.mutex.Lock() + level := l.calculateLevel(loggerName) + l.levelCache[loggerName] = level + l.mutex.Unlock() + + return level +} + +// calculateLevel walks the logger name back to find the appropriate +// log level from the current spec. +func (l *LoggerLevels) calculateLevel(loggerName string) zapcore.Level { + candidate := loggerName + "." + for { + if lvl, ok := l.specs[candidate]; ok { + return lvl + } + + idx := strings.LastIndex(candidate, ".") + if idx <= 0 { + return l.defaultLevel + } + candidate = candidate[:idx] + } +} + +// cachedLevel attempts to retrieve the effective log level for a logger from the +// cache. If the logger is not found, ok will be false. +func (l *LoggerLevels) cachedLevel(loggerName string) (lvl zapcore.Level, ok bool) { + l.mutex.RLock() + level, ok := l.levelCache[loggerName] + l.mutex.RUnlock() + return level, ok +} + +// Spec returns a normalized version of the active logging spec. +func (l *LoggerLevels) Spec() string { + l.mutex.RLock() + defer l.mutex.RUnlock() + + var fields []string + for k, v := range l.specs { + fields = append(fields, fmt.Sprintf("%s=%s", k, v)) + } + + sort.Strings(fields) + fields = append(fields, l.defaultLevel.String()) + + return strings.Join(fields, ":") +} + +// Enabled function is an enabled check that evaluates the minimum active logging level. +// It serves as a fast check before the (relatively) expensive Check call in the core. +func (l *LoggerLevels) Enabled(lvl zapcore.Level) bool { + l.mutex.RLock() + enabled := l.minLevel.Enabled(lvl) + l.mutex.RUnlock() + return enabled +} diff --git a/vendor/github.com/IBM/idemix/common/flogging/logging.go b/vendor/github.com/IBM/idemix/common/flogging/logging.go new file mode 100644 index 000000000..fa416d569 --- /dev/null +++ b/vendor/github.com/IBM/idemix/common/flogging/logging.go @@ -0,0 +1,250 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package flogging + +import ( + "fmt" + "io" + "os" + "sync" + + "github.com/IBM/idemix/common/flogging/fabenc" + zaplogfmt "github.com/sykesm/zap-logfmt" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +// Config is used to provide dependencies to a Logging instance. +type Config struct { + // Format is the log record format specifier for the Logging instance. If the + // spec is the string "json", log records will be formatted as JSON. Any + // other string will be provided to the FormatEncoder. Please see + // fabenc.ParseFormat for details on the supported verbs. + // + // If Format is not provided, a default format that provides basic information will + // be used. + Format string + + // LogSpec determines the log levels that are enabled for the logging system. The + // spec must be in a format that can be processed by ActivateSpec. + // + // If LogSpec is not provided, loggers will be enabled at the INFO level. + LogSpec string + + // Writer is the sink for encoded and formatted log records. + // + // If a Writer is not provided, os.Stderr will be used as the log sink. + Writer io.Writer +} + +// Logging maintains the state associated with the fabric logging system. It is +// intended to bridge between the legacy logging infrastructure built around +// go-logging and the structured, level logging provided by zap. +type Logging struct { + *LoggerLevels + + mutex sync.RWMutex + encoding Encoding + encoderConfig zapcore.EncoderConfig + multiFormatter *fabenc.MultiFormatter + writer zapcore.WriteSyncer + observer Observer +} + +// New creates a new logging system and initializes it with the provided +// configuration. +func New(c Config) (*Logging, error) { + encoderConfig := zap.NewProductionEncoderConfig() + encoderConfig.NameKey = "name" + + l := &Logging{ + LoggerLevels: &LoggerLevels{ + defaultLevel: defaultLevel, + }, + encoderConfig: encoderConfig, + multiFormatter: fabenc.NewMultiFormatter(), + } + + err := l.Apply(c) + if err != nil { + return nil, err + } + return l, nil +} + +// Apply applies the provided configuration to the logging system. +func (l *Logging) Apply(c Config) error { + err := l.SetFormat(c.Format) + if err != nil { + return err + } + + if c.LogSpec == "" { + c.LogSpec = os.Getenv("FABRIC_LOGGING_SPEC") + } + if c.LogSpec == "" { + c.LogSpec = defaultLevel.String() + } + + err = l.LoggerLevels.ActivateSpec(c.LogSpec) + if err != nil { + return err + } + + if c.Writer == nil { + c.Writer = os.Stderr + } + l.SetWriter(c.Writer) + + return nil +} + +// SetFormat updates how log records are formatted and encoded. Log entries +// created after this method has completed will use the new format. +// +// An error is returned if the log format specification cannot be parsed. +func (l *Logging) SetFormat(format string) error { + l.mutex.Lock() + defer l.mutex.Unlock() + if format == "" { + format = defaultFormat + } + + if format == "json" { + l.encoding = JSON + return nil + } + + if format == "logfmt" { + l.encoding = LOGFMT + return nil + } + + formatters, err := fabenc.ParseFormat(format) + if err != nil { + return err + } + l.multiFormatter.SetFormatters(formatters) + l.encoding = CONSOLE + + return nil +} + +// SetWriter controls which writer formatted log records are written to. +// Writers, with the exception of an *os.File, need to be safe for concurrent +// use by multiple go routines. +func (l *Logging) SetWriter(w io.Writer) io.Writer { + var sw zapcore.WriteSyncer + switch t := w.(type) { + case *os.File: + sw = zapcore.Lock(t) + case zapcore.WriteSyncer: + sw = t + default: + sw = zapcore.AddSync(w) + } + + l.mutex.Lock() + ow := l.writer + l.writer = sw + l.mutex.Unlock() + + return ow +} + +// SetObserver is used to provide a log observer that will be called as log +// levels are checked or written.. Only a single observer is supported. +func (l *Logging) SetObserver(observer Observer) Observer { + l.mutex.Lock() + so := l.observer + l.observer = observer + l.mutex.Unlock() + + return so +} + +// Write satisfies the io.Write contract. It delegates to the writer argument +// of SetWriter or the Writer field of Config. The Core uses this when encoding +// log records. +func (l *Logging) Write(b []byte) (int, error) { + l.mutex.RLock() + w := l.writer + l.mutex.RUnlock() + + return w.Write(b) +} + +// Sync satisfies the zapcore.WriteSyncer interface. It is used by the Core to +// flush log records before terminating the process. +func (l *Logging) Sync() error { + l.mutex.RLock() + w := l.writer + l.mutex.RUnlock() + + return w.Sync() +} + +// Encoding satisfies the Encoding interface. It determines whether the JSON or +// CONSOLE encoder should be used by the Core when log records are written. +func (l *Logging) Encoding() Encoding { + l.mutex.RLock() + e := l.encoding + l.mutex.RUnlock() + return e +} + +// ZapLogger instantiates a new zap.Logger with the specified name. The name is +// used to determine which log levels are enabled. +func (l *Logging) ZapLogger(name string) *zap.Logger { + if !isValidLoggerName(name) { + panic(fmt.Sprintf("invalid logger name: %s", name)) + } + + l.mutex.RLock() + core := &Core{ + LevelEnabler: l.LoggerLevels, + Levels: l.LoggerLevels, + Encoders: map[Encoding]zapcore.Encoder{ + JSON: zapcore.NewJSONEncoder(l.encoderConfig), + CONSOLE: fabenc.NewFormatEncoder(l.multiFormatter), + LOGFMT: zaplogfmt.NewEncoder(l.encoderConfig), + }, + Selector: l, + Output: l, + Observer: l, + } + l.mutex.RUnlock() + + return NewZapLogger(core).Named(name) +} + +func (l *Logging) Check(e zapcore.Entry, ce *zapcore.CheckedEntry) { + l.mutex.RLock() + observer := l.observer + l.mutex.RUnlock() + + if observer != nil { + observer.Check(e, ce) + } +} + +func (l *Logging) WriteEntry(e zapcore.Entry, fields []zapcore.Field) { + l.mutex.RLock() + observer := l.observer + l.mutex.RUnlock() + + if observer != nil { + observer.WriteEntry(e, fields) + } +} + +// Logger instantiates a new FabricLogger with the specified name. The name is +// used to determine which log levels are enabled. +func (l *Logging) Logger(name string) *FabricLogger { + zl := l.ZapLogger(name) + return NewFabricLogger(zl) +} diff --git a/vendor/github.com/IBM/idemix/common/flogging/zap.go b/vendor/github.com/IBM/idemix/common/flogging/zap.go new file mode 100644 index 000000000..6319833bc --- /dev/null +++ b/vendor/github.com/IBM/idemix/common/flogging/zap.go @@ -0,0 +1,105 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package flogging + +import ( + "fmt" + "strings" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "go.uber.org/zap/zapgrpc" +) + +// NewZapLogger creates a zap logger around a new zap.Core. The core will use +// the provided encoder and sinks and a level enabler that is associated with +// the provided logger name. The logger that is returned will be named the same +// as the logger. +func NewZapLogger(core zapcore.Core, options ...zap.Option) *zap.Logger { + return zap.New( + core, + append([]zap.Option{ + zap.AddCaller(), + zap.AddStacktrace(zapcore.ErrorLevel), + }, options...)..., + ) +} + +// NewGRPCLogger creates a grpc.Logger that delegates to a zap.Logger. +func NewGRPCLogger(l *zap.Logger) *zapgrpc.Logger { + l = l.WithOptions( + zap.AddCaller(), + zap.AddCallerSkip(3), + ) + return zapgrpc.NewLogger(l, zapgrpc.WithDebug()) +} + +// NewFabricLogger creates a logger that delegates to the zap.SugaredLogger. +func NewFabricLogger(l *zap.Logger, options ...zap.Option) *FabricLogger { + return &FabricLogger{ + s: l.WithOptions(append(options, zap.AddCallerSkip(1))...).Sugar(), + } +} + +// A FabricLogger is an adapter around a zap.SugaredLogger that provides +// structured logging capabilities while preserving much of the legacy logging +// behavior. +// +// The most significant difference between the FabricLogger and the +// zap.SugaredLogger is that methods without a formatting suffix (f or w) build +// the log entry message with fmt.Sprintln instead of fmt.Sprint. Without this +// change, arguments are not separated by spaces. +type FabricLogger struct{ s *zap.SugaredLogger } + +func (f *FabricLogger) DPanic(args ...interface{}) { f.s.DPanicf(formatArgs(args)) } +func (f *FabricLogger) DPanicf(template string, args ...interface{}) { f.s.DPanicf(template, args...) } +func (f *FabricLogger) DPanicw(msg string, kvPairs ...interface{}) { f.s.DPanicw(msg, kvPairs...) } +func (f *FabricLogger) Debug(args ...interface{}) { f.s.Debugf(formatArgs(args)) } +func (f *FabricLogger) Debugf(template string, args ...interface{}) { f.s.Debugf(template, args...) } +func (f *FabricLogger) Debugw(msg string, kvPairs ...interface{}) { f.s.Debugw(msg, kvPairs...) } +func (f *FabricLogger) Error(args ...interface{}) { f.s.Errorf(formatArgs(args)) } +func (f *FabricLogger) Errorf(template string, args ...interface{}) { f.s.Errorf(template, args...) } +func (f *FabricLogger) Errorw(msg string, kvPairs ...interface{}) { f.s.Errorw(msg, kvPairs...) } +func (f *FabricLogger) Fatal(args ...interface{}) { f.s.Fatalf(formatArgs(args)) } +func (f *FabricLogger) Fatalf(template string, args ...interface{}) { f.s.Fatalf(template, args...) } +func (f *FabricLogger) Fatalw(msg string, kvPairs ...interface{}) { f.s.Fatalw(msg, kvPairs...) } +func (f *FabricLogger) Info(args ...interface{}) { f.s.Infof(formatArgs(args)) } +func (f *FabricLogger) Infof(template string, args ...interface{}) { f.s.Infof(template, args...) } +func (f *FabricLogger) Infow(msg string, kvPairs ...interface{}) { f.s.Infow(msg, kvPairs...) } +func (f *FabricLogger) Panic(args ...interface{}) { f.s.Panicf(formatArgs(args)) } +func (f *FabricLogger) Panicf(template string, args ...interface{}) { f.s.Panicf(template, args...) } +func (f *FabricLogger) Panicw(msg string, kvPairs ...interface{}) { f.s.Panicw(msg, kvPairs...) } +func (f *FabricLogger) Warn(args ...interface{}) { f.s.Warnf(formatArgs(args)) } +func (f *FabricLogger) Warnf(template string, args ...interface{}) { f.s.Warnf(template, args...) } +func (f *FabricLogger) Warnw(msg string, kvPairs ...interface{}) { f.s.Warnw(msg, kvPairs...) } +func (f *FabricLogger) Warning(args ...interface{}) { f.s.Warnf(formatArgs(args)) } +func (f *FabricLogger) Warningf(template string, args ...interface{}) { f.s.Warnf(template, args...) } + +// for backwards compatibility +func (f *FabricLogger) Critical(args ...interface{}) { f.s.Errorf(formatArgs(args)) } +func (f *FabricLogger) Criticalf(template string, args ...interface{}) { f.s.Errorf(template, args...) } +func (f *FabricLogger) Notice(args ...interface{}) { f.s.Infof(formatArgs(args)) } +func (f *FabricLogger) Noticef(template string, args ...interface{}) { f.s.Infof(template, args...) } + +func (f *FabricLogger) Named(name string) *FabricLogger { return &FabricLogger{s: f.s.Named(name)} } +func (f *FabricLogger) Sync() error { return f.s.Sync() } +func (f *FabricLogger) Zap() *zap.Logger { return f.s.Desugar() } + +func (f *FabricLogger) IsEnabledFor(level zapcore.Level) bool { + return f.s.Desugar().Core().Enabled(level) +} + +func (f *FabricLogger) With(args ...interface{}) *FabricLogger { + return &FabricLogger{s: f.s.With(args...)} +} + +func (f *FabricLogger) WithOptions(opts ...zap.Option) *FabricLogger { + l := f.s.Desugar().WithOptions(opts...) + return &FabricLogger{s: l.Sugar()} +} + +func formatArgs(args []interface{}) string { return strings.TrimSuffix(fmt.Sprintln(args...), "\n") } diff --git a/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/LICENSE b/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/LICENSE new file mode 100644 index 000000000..8f71f43fe --- /dev/null +++ b/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/bbs12381g2pub.go b/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/bbs12381g2pub.go new file mode 100644 index 000000000..6b4353295 --- /dev/null +++ b/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/bbs12381g2pub.go @@ -0,0 +1,338 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +// Package bbs12381g2pub contains BBS+ signing primitives and keys. Although it can be used directly, it is recommended +// to use BBS+ keys created by the kms along with the framework's Crypto service. +// +// The default local Crypto service is found at: +// "github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/tinkcrypto" +// +// While the remote Crypto service is found at: +// "github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/webkms" +package bbs12381g2pub + +import ( + "errors" + "fmt" + "sort" + + ml "github.com/IBM/mathlib" +) + +// nolint:gochecknoglobals +var curve = ml.Curves[ml.BLS12_381_BBS] + +// BBSG2Pub defines BBS+ signature scheme where public key is a point in the field of G2. +// BBS+ signature scheme (as defined in https://eprint.iacr.org/2016/663.pdf, section 4.3). +type BBSG2Pub struct{} + +// New creates a new BBSG2Pub. +func New() *BBSG2Pub { + return &BBSG2Pub{} +} + +// Number of bytes in scalar compressed form. +const frCompressedSize = 32 + +var ( + // nolint:gochecknoglobals + // Signature length. + bls12381SignatureLen = curve.CompressedG1ByteSize + 2*frCompressedSize + + // nolint:gochecknoglobals + // Default BLS 12-381 public key length in G2 field. + bls12381G2PublicKeyLen = curve.CompressedG2ByteSize + + // nolint:gochecknoglobals + // Number of bytes in G1 X coordinate. + g1CompressedSize = curve.CompressedG1ByteSize + + // nolint:gochecknoglobals + // Number of bytes in G1 X and Y coordinates. + g1UncompressedSize = curve.G1ByteSize + + // nolint:gochecknoglobals + // Number of bytes in G2 X(a, b) and Y(a, b) coordinates. + g2UncompressedSize = curve.G2ByteSize + + // nolint:gochecknoglobals + // Number of bytes in scalar uncompressed form. + frUncompressedSize = curve.ScalarByteSize +) + +// Verify makes BLS BBS12-381 signature verification. +func (bbs *BBSG2Pub) Verify(messages [][]byte, sigBytes, pubKeyBytes []byte) error { + signature, err := ParseSignature(sigBytes) + if err != nil { + return fmt.Errorf("parse signature: %w", err) + } + + pubKey, err := UnmarshalPublicKey(pubKeyBytes) + if err != nil { + return fmt.Errorf("parse public key: %w", err) + } + + messagesCount := len(messages) + + publicKeyWithGenerators, err := pubKey.ToPublicKeyWithGenerators(messagesCount) + if err != nil { + return fmt.Errorf("build generators from public key: %w", err) + } + + messagesFr := messagesToFr(messages) + + return signature.Verify(messagesFr, publicKeyWithGenerators) +} + +// Sign signs the one or more messages using private key in compressed form. +func (bbs *BBSG2Pub) Sign(messages [][]byte, privKeyBytes []byte) ([]byte, error) { + privKey, err := UnmarshalPrivateKey(privKeyBytes) + if err != nil { + return nil, fmt.Errorf("unmarshal private key: %w", err) + } + + if len(messages) == 0 { + return nil, errors.New("messages are not defined") + } + + return bbs.SignWithKey(messages, nil, privKey) +} + +// VerifyProof verifies BBS+ signature proof for one ore more revealed messages. +func (bbs *BBSG2Pub) VerifyProof(messagesBytes [][]byte, proof, nonce, pubKeyBytes []byte) error { + + messages := messagesToFr(messagesBytes) + + return bbs.VerifyProofFr(messages, proof, nonce, pubKeyBytes) +} + +// VerifyProof verifies BBS+ signature proof for one ore more revealed messages. +func (bbs *BBSG2Pub) VerifyProofFr(messages []*SignatureMessage, proof, nonce, pubKeyBytes []byte) error { + payload, err := ParsePoKPayload(proof) + if err != nil { + return fmt.Errorf("parse signature proof: %w", err) + } + + signatureProof, err := ParseSignatureProof(proof[payload.LenInBytes():]) + if err != nil { + return fmt.Errorf("parse signature proof: %w", err) + } + + pubKey, err := UnmarshalPublicKey(pubKeyBytes) + if err != nil { + return fmt.Errorf("parse public key: %w", err) + } + + publicKeyWithGenerators, err := pubKey.ToPublicKeyWithGenerators(payload.messagesCount) + if err != nil { + return fmt.Errorf("build generators from public key: %w", err) + } + + if len(payload.Revealed) > len(messages) { + return fmt.Errorf("payload revealed bigger from messages") + } + + revealedMessages := make(map[int]*SignatureMessage) + for i := range payload.Revealed { + revealedMessages[payload.Revealed[i]] = messages[i] + } + + challengeBytes := signatureProof.GetBytesForChallenge(revealedMessages, publicKeyWithGenerators) + proofNonce := ParseProofNonce(nonce) + proofNonceBytes := proofNonce.ToBytes() + challengeBytes = append(challengeBytes, proofNonceBytes...) + proofChallenge := FrFromOKM(challengeBytes) + + return signatureProof.Verify(proofChallenge, publicKeyWithGenerators, revealedMessages, messages) +} + +// DeriveProof derives a proof of BBS+ signature with some messages disclosed. +func (bbs *BBSG2Pub) DeriveProof(messages [][]byte, sigBytes, nonce, pubKeyBytes []byte, + revealedIndexes []int) ([]byte, error) { + + return bbs.DeriveProofZr(messagesToFr(messages), sigBytes, nonce, pubKeyBytes, revealedIndexes) +} + +// DeriveProof derives a proof of BBS+ signature with some messages disclosed. +func (bbs *BBSG2Pub) DeriveProofZr(messagesFr []*SignatureMessage, sigBytes, nonce, pubKeyBytes []byte, + revealedIndexes []int) ([]byte, error) { + + if len(revealedIndexes) == 0 { + return nil, errors.New("no message to reveal") + } + + sort.Ints(revealedIndexes) + + messagesCount := len(messagesFr) + + pubKey, err := UnmarshalPublicKey(pubKeyBytes) + if err != nil { + return nil, fmt.Errorf("parse public key: %w", err) + } + + publicKeyWithGenerators, err := pubKey.ToPublicKeyWithGenerators(messagesCount) + if err != nil { + return nil, fmt.Errorf("build generators from public key: %w", err) + } + + signature, err := ParseSignature(sigBytes) + if err != nil { + return nil, fmt.Errorf("parse signature: %w", err) + } + + pokSignature, err := NewPoKOfSignature(signature, messagesFr, revealedIndexes, publicKeyWithGenerators) + if err != nil { + return nil, fmt.Errorf("init proof of knowledge signature: %w", err) + } + + challengeBytes := pokSignature.ToBytes() + + proofNonce := ParseProofNonce(nonce) + proofNonceBytes := proofNonce.ToBytes() + challengeBytes = append(challengeBytes, proofNonceBytes...) + + proofChallenge := FrFromOKM(challengeBytes) + + proof := pokSignature.GenerateProof(proofChallenge) + + payload := NewPoKPayload(messagesCount, revealedIndexes) + + payloadBytes, err := payload.ToBytes() + if err != nil { + return nil, fmt.Errorf("derive proof: paylod to bytes: %w", err) + } + + signatureProofBytes := append(payloadBytes, proof.ToBytes()...) + + return signatureProofBytes, nil +} + +// SignWithKey signs the one or more messages using BBS+ key pair. +func (bbs *BBSG2Pub) SignWithKey(messages [][]byte, commitment *ml.G1, privKey *PrivateKey) ([]byte, error) { + + messagesFr := make([]*SignatureMessage, 0, len(messages)) + + for i, msg := range messages { + if len(msg) == 0 { + continue + } + + messagesFr = append(messagesFr, ParseSignatureMessage(messages[i], i)) + } + + return bbs.SignWithKeyFr(messagesFr, len(messages), commitment, privKey) +} + +// SignWithKey signs the one or more messages using BBS+ key pair. +func (bbs *BBSG2Pub) SignWithKeyFr(messagesFr []*SignatureMessage, messagesCount int, commitment *ml.G1, privKey *PrivateKey) ([]byte, error) { + var err error + + pubKey := privKey.PublicKey() + + pubKeyWithGenerators, err := pubKey.ToPublicKeyWithGenerators(messagesCount) + if err != nil { + return nil, fmt.Errorf("build generators from public key: %w", err) + } + + e, s := createRandSignatureFr(), createRandSignatureFr() + exp := privKey.FR.Copy() + exp = exp.Plus(e) + exp.InvModP(curve.GroupOrder) + + b := computeB(s, messagesFr, pubKeyWithGenerators) + if len(messagesFr) != messagesCount { + b.Add(commitment) + } + + sig := b.Mul(frToRepr(exp)) + + signature := &Signature{ + A: sig, + E: e, + S: s, + } + + return signature.ToBytes() +} + +func computeB(s *ml.Zr, messages []*SignatureMessage, key *PublicKeyWithGenerators) *ml.G1 { + const basesOffset = 2 + + cb := NewCommitmentBuilder(len(messages) + basesOffset) + + cb.Add(curve.GenG1, curve.NewZrFromInt(1)) + cb.Add(key.H0, s) + + for i := 0; i < len(messages); i++ { + cb.Add(key.H[messages[i].Idx], messages[i].FR) + } + + return cb.Build() +} + +type commitmentBuilder struct { + bases []*ml.G1 + scalars []*ml.Zr +} + +func NewCommitmentBuilder(expectedSize int) *commitmentBuilder { + return &commitmentBuilder{ + bases: make([]*ml.G1, 0, expectedSize), + scalars: make([]*ml.Zr, 0, expectedSize), + } +} + +func (cb *commitmentBuilder) Add(base *ml.G1, scalar *ml.Zr) { + cb.bases = append(cb.bases, base) + cb.scalars = append(cb.scalars, scalar) +} + +func (cb *commitmentBuilder) Build() *ml.G1 { + return sumOfG1Products(cb.bases, cb.scalars) +} + +func sumOfG1Products(bases []*ml.G1, scalars []*ml.Zr) *ml.G1 { + var res *ml.G1 + + for i := 0; i < len(bases); i++ { + b := bases[i] + s := scalars[i] + + g := b.Mul(frToRepr(s)) + if res == nil { + res = g + } else { + res.Add(g) + } + } + + return res +} + +func compareTwoPairings(p1 *ml.G1, q1 *ml.G2, + p2 *ml.G1, q2 *ml.G2) bool { + p := curve.Pairing2(q1, p1, q2, p2) + p = curve.FExp(p) + + return p.IsUnity() +} + +// ProofNonce is a nonce for Proof of Knowledge proof. +type ProofNonce struct { + fr *ml.Zr +} + +// ParseProofNonce creates a new ProofNonce from bytes. +func ParseProofNonce(proofNonceBytes []byte) *ProofNonce { + return &ProofNonce{ + FrFromOKM(proofNonceBytes), + } +} + +// ToBytes converts ProofNonce into bytes. +func (pn *ProofNonce) ToBytes() []byte { + return frToRepr(pn.fr).Bytes() +} diff --git a/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/fr.go b/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/fr.go new file mode 100644 index 000000000..c43a2a366 --- /dev/null +++ b/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/fr.go @@ -0,0 +1,71 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package bbs12381g2pub + +import ( + "crypto/rand" + + ml "github.com/IBM/mathlib" + "golang.org/x/crypto/blake2b" +) + +func parseFr(data []byte) *ml.Zr { + return curve.NewZrFromBytes(data) +} + +// nolint:gochecknoglobals +var f2192Bytes = []byte{ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +} + +func f2192() *ml.Zr { + return curve.NewZrFromBytes(f2192Bytes) +} + +func FrFromOKM(message []byte) *ml.Zr { + const ( + eightBytes = 8 + okmMiddle = 24 + ) + + // We pass a null key so error is impossible here. + h, _ := blake2b.New384(nil) //nolint:errcheck + + // blake2b.digest() does not return an error. + _, _ = h.Write(message) + okm := h.Sum(nil) + emptyEightBytes := make([]byte, eightBytes) + + elm := curve.NewZrFromBytes(append(emptyEightBytes, okm[:okmMiddle]...)) + elm = elm.Mul(f2192()) + + fr := curve.NewZrFromBytes(append(emptyEightBytes, okm[okmMiddle:]...)) + elm = elm.Plus(fr) + + return elm +} + +func frToRepr(fr *ml.Zr) *ml.Zr { + return fr.Copy() +} + +func messagesToFr(messages [][]byte) []*SignatureMessage { + messagesFr := make([]*SignatureMessage, len(messages)) + + for i := range messages { + messagesFr[i] = ParseSignatureMessage(messages[i], i) + } + + return messagesFr +} + +func createRandSignatureFr() *ml.Zr { + return curve.NewRandomZr(rand.Reader) +} diff --git a/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/keys.go b/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/keys.go new file mode 100644 index 000000000..dd6da5bd6 --- /dev/null +++ b/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/keys.go @@ -0,0 +1,196 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package bbs12381g2pub + +import ( + "crypto/rand" + "errors" + "fmt" + "hash" + "io" + + ml "github.com/IBM/mathlib" + "golang.org/x/crypto/hkdf" +) + +var ( + // nolint:gochecknoglobals + seedSize = frCompressedSize + + // nolint:gochecknoglobals + generateKeySalt = "BBS-SIG-KEYGEN-SALT-" +) + +// PublicKey defines BLS Public Key. +type PublicKey struct { + PointG2 *ml.G2 +} + +// PrivateKey defines BLS Public Key. +type PrivateKey struct { + FR *ml.Zr +} + +// PublicKeyWithGenerators extends PublicKey with a blinding generator h0, a commitment to the secret key w, +// and a generator for each message h. +type PublicKeyWithGenerators struct { + H0 *ml.G1 + H []*ml.G1 + + w *ml.G2 + + messagesCount int +} + +// ToPublicKeyWithGenerators creates PublicKeyWithGenerators from the PublicKey. +func (pk *PublicKey) ToPublicKeyWithGenerators(messagesCount int) (*PublicKeyWithGenerators, error) { + offset := g2UncompressedSize + 1 + + data := calcData(pk, messagesCount) + + h0 := hashToG1(data) + + h := make([]*ml.G1, messagesCount) + + for i := 1; i <= messagesCount; i++ { + dataCopy := make([]byte, len(data)) + copy(dataCopy, data) + + iBytes := uint32ToBytes(uint32(i)) + + for j := 0; j < len(iBytes); j++ { + dataCopy[j+offset] = iBytes[j] + } + + h[i-1] = hashToG1(dataCopy) + } + + return &PublicKeyWithGenerators{ + H0: h0, + H: h, + w: pk.PointG2, + messagesCount: messagesCount, + }, nil +} + +func calcData(key *PublicKey, messagesCount int) []byte { + data := key.PointG2.Bytes() + + data = append(data, 0, 0, 0, 0, 0, 0) + + mcBytes := uint32ToBytes(uint32(messagesCount)) + + data = append(data, mcBytes...) + + return data +} + +func hashToG1(data []byte) *ml.G1 { + var dstG1 = []byte("BLS12381G1_XMD:BLAKE2B_SSWU_RO_BBS+_SIGNATURES:1_0_0") + + return curve.HashToG1WithDomain(data, dstG1) +} + +// UnmarshalPrivateKey unmarshals PrivateKey. +func UnmarshalPrivateKey(privKeyBytes []byte) (*PrivateKey, error) { + if len(privKeyBytes) != frCompressedSize { + return nil, errors.New("invalid size of private key") + } + + fr := parseFr(privKeyBytes) + + return &PrivateKey{ + FR: fr, + }, nil +} + +// Marshal marshals PrivateKey. +func (k *PrivateKey) Marshal() ([]byte, error) { + bytes := k.FR.Bytes() + return bytes, nil +} + +// PublicKey returns a Public Key as G2 point generated from the Private Key. +func (k *PrivateKey) PublicKey() *PublicKey { + pointG2 := curve.GenG2.Mul(frToRepr(k.FR)) + + return &PublicKey{pointG2} +} + +// UnmarshalPublicKey parses a PublicKey from bytes. +func UnmarshalPublicKey(pubKeyBytes []byte) (*PublicKey, error) { + if len(pubKeyBytes) != bls12381G2PublicKeyLen { + return nil, errors.New("invalid size of public key") + } + + pointG2, err := curve.NewG2FromCompressed(pubKeyBytes) + if err != nil { + return nil, fmt.Errorf("deserialize public key: %w", err) + } + + return &PublicKey{ + PointG2: pointG2, + }, nil +} + +// Marshal marshals PublicKey. +func (pk *PublicKey) Marshal() ([]byte, error) { + pkBytes := pk.PointG2.Compressed() + + return pkBytes, nil +} + +// GenerateKeyPair generates BBS+ PublicKey and PrivateKey pair. +func GenerateKeyPair(h func() hash.Hash, seed []byte) (*PublicKey, *PrivateKey, error) { + if len(seed) != 0 && len(seed) != seedSize { + return nil, nil, errors.New("invalid size of seed") + } + + okm, err := generateOKM(seed, h) + if err != nil { + return nil, nil, err + } + + privKeyFr := FrFromOKM(okm) + + privKey := &PrivateKey{privKeyFr} + pubKey := privKey.PublicKey() + + return pubKey, privKey, nil +} + +func generateOKM(ikm []byte, h func() hash.Hash) ([]byte, error) { + salt := []byte(generateKeySalt) + info := make([]byte, 2) + + if ikm != nil { + ikm = append(ikm, 0) + } else { + ikm = make([]byte, seedSize+1) + + _, err := rand.Read(ikm) + if err != nil { + return nil, err + } + + ikm[seedSize] = 0 + } + + return newHKDF(h, ikm, salt, info, frUncompressedSize) +} + +func newHKDF(h func() hash.Hash, ikm, salt, info []byte, length int) ([]byte, error) { + reader := hkdf.New(h, ikm, salt, info) + result := make([]byte, length) + + _, err := io.ReadFull(reader, result) + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/proof_of_knowledge.go b/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/proof_of_knowledge.go new file mode 100644 index 000000000..eb29f44e0 --- /dev/null +++ b/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/proof_of_knowledge.go @@ -0,0 +1,230 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package bbs12381g2pub + +import ( + "fmt" + + ml "github.com/IBM/mathlib" +) + +// PoKOfSignature is Proof of Knowledge of a Signature that is used by the prover to construct PoKOfSignatureProof. +type PoKOfSignature struct { + aPrime *ml.G1 + aBar *ml.G1 + d *ml.G1 + + pokVC1 *ProverCommittedG1 + secrets1 []*ml.Zr + + PokVC2 *ProverCommittedG1 + secrets2 []*ml.Zr + + revealedMessages map[int]*SignatureMessage +} + +// NewPoKOfSignature creates a new PoKOfSignature. +func NewPoKOfSignature(signature *Signature, messages []*SignatureMessage, revealedIndexes []int, + pubKey *PublicKeyWithGenerators) (*PoKOfSignature, error) { + err := signature.Verify(messages, pubKey) + if err != nil { + return nil, fmt.Errorf("verify input signature: %w", err) + } + + r1, r2 := createRandSignatureFr(), createRandSignatureFr() + b := computeB(signature.S, messages, pubKey) + aPrime := signature.A.Mul(frToRepr(r1)) + + aBarDenom := aPrime.Mul(frToRepr(signature.E)) + + aBar := b.Mul(frToRepr(r1)) + aBar.Sub(aBarDenom) + + r2D := r2.Copy() + r2D.Neg() + + commitmentBasesCount := 2 + cb := NewCommitmentBuilder(commitmentBasesCount) + cb.Add(b, r1) + cb.Add(pubKey.H0, r2D) + + d := cb.Build() + r3 := r1.Copy() + r3.InvModP(curve.GroupOrder) + + sPrime := r2.Mul(r3) + sPrime.Neg() + sPrime = sPrime.Plus(signature.S) + + pokVC1, secrets1 := newVC1Signature(aPrime, pubKey.H0, signature.E, r2) + + revealedMessages := make(map[int]*SignatureMessage, len(revealedIndexes)) + + if len(messages) < len(revealedIndexes) { + return nil, fmt.Errorf("invalid size: %d revealed indexes is larger than %d messages", len(revealedIndexes), + len(messages)) + } + + for _, ind := range revealedIndexes { + revealedMessages[ind] = messages[ind] + } + + pokVC2, secrets2 := newVC2Signature(d, r3, pubKey, sPrime, messages, revealedMessages) + + return &PoKOfSignature{ + aPrime: aPrime, + aBar: aBar, + d: d, + pokVC1: pokVC1, + secrets1: secrets1, + PokVC2: pokVC2, + secrets2: secrets2, + revealedMessages: revealedMessages, + }, nil +} + +func newVC1Signature(aPrime *ml.G1, h0 *ml.G1, + e, r2 *ml.Zr) (*ProverCommittedG1, []*ml.Zr) { + committing1 := NewProverCommittingG1() + secrets1 := make([]*ml.Zr, 2) + + committing1.Commit(aPrime) + + sigE := e.Copy() + sigE.Neg() + secrets1[0] = sigE + + committing1.Commit(h0) + + secrets1[1] = r2 + pokVC1 := committing1.Finish() + + return pokVC1, secrets1 +} + +func newVC2Signature(d *ml.G1, r3 *ml.Zr, pubKey *PublicKeyWithGenerators, sPrime *ml.Zr, + messages []*SignatureMessage, revealedMessages map[int]*SignatureMessage) (*ProverCommittedG1, []*ml.Zr) { + messagesCount := len(messages) + committing2 := NewProverCommittingG1() + baseSecretsCount := 2 + secrets2 := make([]*ml.Zr, 0, baseSecretsCount+messagesCount) + + committing2.Commit(d) + + r3D := r3.Copy() + r3D.Neg() + + secrets2 = append(secrets2, r3D) + + committing2.Commit(pubKey.H0) + + secrets2 = append(secrets2, sPrime) + + for i := 0; i < messagesCount; i++ { + if _, ok := revealedMessages[i]; ok { + continue + } + + committing2.Commit(pubKey.H[i]) + + sourceFR := messages[i].FR + hiddenFRCopy := sourceFR.Copy() + + secrets2 = append(secrets2, hiddenFRCopy) + } + + pokVC2 := committing2.Finish() + + return pokVC2, secrets2 +} + +// ToBytes converts PoKOfSignature to bytes. +func (pos *PoKOfSignature) ToBytes() []byte { + challengeBytes := pos.aBar.Bytes() + challengeBytes = append(challengeBytes, pos.pokVC1.ToBytes()...) + challengeBytes = append(challengeBytes, pos.PokVC2.ToBytes()...) + + return challengeBytes +} + +// GenerateProof generates PoKOfSignatureProof proof from PoKOfSignature signature. +func (pos *PoKOfSignature) GenerateProof(challengeHash *ml.Zr) *PoKOfSignatureProof { + return &PoKOfSignatureProof{ + aPrime: pos.aPrime, + aBar: pos.aBar, + d: pos.d, + proofVC1: pos.pokVC1.GenerateProof(challengeHash, pos.secrets1), + ProofVC2: pos.PokVC2.GenerateProof(challengeHash, pos.secrets2), + } +} + +// ProverCommittedG1 helps to generate a ProofG1. +type ProverCommittedG1 struct { + Bases []*ml.G1 + BlindingFactors []*ml.Zr + Commitment *ml.G1 +} + +// ToBytes converts ProverCommittedG1 to bytes. +func (g *ProverCommittedG1) ToBytes() []byte { + bytes := make([]byte, 0) + + for _, base := range g.Bases { + bytes = append(bytes, base.Bytes()...) + } + + return append(bytes, g.Commitment.Bytes()...) +} + +// GenerateProof generates proof ProofG1 for all secrets. +func (g *ProverCommittedG1) GenerateProof(challenge *ml.Zr, secrets []*ml.Zr) *ProofG1 { + responses := make([]*ml.Zr, len(g.Bases)) + + for i := range g.BlindingFactors { + c := challenge.Mul(secrets[i]) + + s := g.BlindingFactors[i].Minus(c) + responses[i] = s + } + + return &ProofG1{ + Commitment: g.Commitment, + Responses: responses, + } +} + +// ProverCommittingG1 is a proof of knowledge of messages in a vector commitment. +type ProverCommittingG1 struct { + bases []*ml.G1 + BlindingFactors []*ml.Zr +} + +// NewProverCommittingG1 creates a new ProverCommittingG1. +func NewProverCommittingG1() *ProverCommittingG1 { + return &ProverCommittingG1{ + bases: make([]*ml.G1, 0), + BlindingFactors: make([]*ml.Zr, 0), + } +} + +// Commit append a base point and randomly generated blinding factor. +func (pc *ProverCommittingG1) Commit(base *ml.G1) { + pc.bases = append(pc.bases, base) + r := createRandSignatureFr() + pc.BlindingFactors = append(pc.BlindingFactors, r) +} + +// Finish helps to generate ProverCommittedG1 after commitment of all base points. +func (pc *ProverCommittingG1) Finish() *ProverCommittedG1 { + commitment := sumOfG1Products(pc.bases, pc.BlindingFactors) + + return &ProverCommittedG1{ + Bases: pc.bases, + BlindingFactors: pc.BlindingFactors, + Commitment: commitment, + } +} diff --git a/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/signature.go b/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/signature.go new file mode 100644 index 000000000..3491e48e4 --- /dev/null +++ b/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/signature.go @@ -0,0 +1,70 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package bbs12381g2pub + +import ( + "errors" + "fmt" + + ml "github.com/IBM/mathlib" +) + +// Signature defines BLS signature. +type Signature struct { + A *ml.G1 + E *ml.Zr + S *ml.Zr +} + +// ParseSignature parses a Signature from bytes. +func ParseSignature(sigBytes []byte) (*Signature, error) { + if len(sigBytes) != bls12381SignatureLen { + return nil, errors.New("invalid size of signature") + } + + pointG1, err := curve.NewG1FromCompressed(sigBytes[:g1CompressedSize]) + if err != nil { + return nil, fmt.Errorf("deserialize G1 compressed signature: %w", err) + } + + e := parseFr(sigBytes[g1CompressedSize : g1CompressedSize+frCompressedSize]) + s := parseFr(sigBytes[g1CompressedSize+frCompressedSize:]) + + return &Signature{ + A: pointG1, + E: e, + S: s, + }, nil +} + +// ToBytes converts signature to bytes using compression of G1 point and E, S FR points. +func (s *Signature) ToBytes() ([]byte, error) { + bytes := make([]byte, bls12381SignatureLen) + + copy(bytes, s.A.Compressed()) + copy(bytes[g1CompressedSize:g1CompressedSize+frCompressedSize], s.E.Bytes()) + copy(bytes[g1CompressedSize+frCompressedSize:], s.S.Bytes()) + + return bytes, nil +} + +// Verify is used for signature verification. +func (s *Signature) Verify(messages []*SignatureMessage, pubKey *PublicKeyWithGenerators) error { + p1 := s.A + + q1 := curve.GenG2.Mul(frToRepr(s.E)) + q1.Add(pubKey.w) + + p2 := computeB(s.S, messages, pubKey) + p2.Neg() + + if compareTwoPairings(p1, q1, p2, curve.GenG2) { + return nil + } + + return errors.New("invalid BLS12-381 signature") +} diff --git a/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/signature_message.go b/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/signature_message.go new file mode 100644 index 000000000..ec8d9cd19 --- /dev/null +++ b/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/signature_message.go @@ -0,0 +1,27 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package bbs12381g2pub + +import ( + ml "github.com/IBM/mathlib" +) + +// SignatureMessage defines a message to be used for a signature check. +type SignatureMessage struct { + FR *ml.Zr + Idx int +} + +// ParseSignatureMessage parses SignatureMessage from bytes. +func ParseSignatureMessage(message []byte, idx int) *SignatureMessage { + elm := FrFromOKM(message) + + return &SignatureMessage{ + FR: elm, + Idx: idx, + } +} diff --git a/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/signature_proof.go b/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/signature_proof.go new file mode 100644 index 000000000..3a13079ef --- /dev/null +++ b/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/signature_proof.go @@ -0,0 +1,276 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package bbs12381g2pub + +import ( + "encoding/binary" + "errors" + "fmt" + + ml "github.com/IBM/mathlib" +) + +// PoKOfSignatureProof defines BLS signature proof. +// It is the actual proof that is sent from prover to verifier. +type PoKOfSignatureProof struct { + aPrime *ml.G1 + aBar *ml.G1 + d *ml.G1 + + proofVC1 *ProofG1 + ProofVC2 *ProofG1 +} + +// GetBytesForChallenge creates bytes for proof challenge. +func (sp *PoKOfSignatureProof) GetBytesForChallenge(revealedMessages map[int]*SignatureMessage, + pubKey *PublicKeyWithGenerators) []byte { + hiddenCount := pubKey.messagesCount - len(revealedMessages) + + bytesLen := (7 + hiddenCount) * g1UncompressedSize //nolint:gomnd + bytes := make([]byte, 0, bytesLen) + + bytes = append(bytes, sp.aBar.Bytes()...) + bytes = append(bytes, sp.aPrime.Bytes()...) + bytes = append(bytes, pubKey.H0.Bytes()...) + bytes = append(bytes, sp.proofVC1.Commitment.Bytes()...) + bytes = append(bytes, sp.d.Bytes()...) + bytes = append(bytes, pubKey.H0.Bytes()...) + + for i := range pubKey.H { + if _, ok := revealedMessages[i]; !ok { + bytes = append(bytes, pubKey.H[i].Bytes()...) + } + } + + bytes = append(bytes, sp.ProofVC2.Commitment.Bytes()...) + + return bytes +} + +// Verify verifies PoKOfSignatureProof. +func (sp *PoKOfSignatureProof) Verify(challenge *ml.Zr, pubKey *PublicKeyWithGenerators, + revealedMessages map[int]*SignatureMessage, messages []*SignatureMessage) error { + aBar := sp.aBar.Copy() + aBar.Neg() + + ok := compareTwoPairings(sp.aPrime, pubKey.w, aBar, curve.GenG2) + if !ok { + return errors.New("bad signature") + } + + err := sp.verifyVC1Proof(challenge, pubKey) + if err != nil { + return err + } + + return sp.verifyVC2Proof(challenge, pubKey, revealedMessages, messages) +} + +func (sp *PoKOfSignatureProof) verifyVC1Proof(challenge *ml.Zr, pubKey *PublicKeyWithGenerators) error { + basesVC1 := []*ml.G1{sp.aPrime, pubKey.H0} + aBarD := sp.aBar.Copy() + aBarD.Sub(sp.d) + + err := sp.proofVC1.Verify(basesVC1, aBarD, challenge) + if err != nil { + return errors.New("bad signature") + } + + return nil +} + +func (sp *PoKOfSignatureProof) verifyVC2Proof(challenge *ml.Zr, pubKey *PublicKeyWithGenerators, + revealedMessages map[int]*SignatureMessage, messages []*SignatureMessage) error { + revealedMessagesCount := len(revealedMessages) + + basesVC2 := make([]*ml.G1, 0, 2+pubKey.messagesCount-revealedMessagesCount) + basesVC2 = append(basesVC2, sp.d, pubKey.H0) + + basesDisclosed := make([]*ml.G1, 0, 1+revealedMessagesCount) + exponents := make([]*ml.Zr, 0, 1+revealedMessagesCount) + + basesDisclosed = append(basesDisclosed, curve.GenG1) + exponents = append(exponents, curve.NewZrFromInt(1)) + + revealedMessagesInd := 0 + + for i := range pubKey.H { + if _, ok := revealedMessages[i]; ok { + basesDisclosed = append(basesDisclosed, pubKey.H[i]) + exponents = append(exponents, messages[revealedMessagesInd].FR) + revealedMessagesInd++ + } else { + basesVC2 = append(basesVC2, pubKey.H[i]) + } + } + + // TODO: expose 0 + pr := curve.GenG1.Copy() + pr.Sub(curve.GenG1) + + for i := 0; i < len(basesDisclosed); i++ { + b := basesDisclosed[i] + s := exponents[i] + + g := b.Mul(frToRepr(s)) + pr.Add(g) + } + + pr.Neg() + + err := sp.ProofVC2.Verify(basesVC2, pr, challenge) + if err != nil { + return errors.New("bad signature") + } + + return nil +} + +// ToBytes converts PoKOfSignatureProof to bytes. +func (sp *PoKOfSignatureProof) ToBytes() []byte { + bytes := make([]byte, 0) + + bytes = append(bytes, sp.aPrime.Compressed()...) + bytes = append(bytes, sp.aBar.Compressed()...) + bytes = append(bytes, sp.d.Compressed()...) + + proof1Bytes := sp.proofVC1.ToBytes() + lenBytes := make([]byte, 4) + binary.BigEndian.PutUint32(lenBytes, uint32(len(proof1Bytes))) + bytes = append(bytes, lenBytes...) + bytes = append(bytes, proof1Bytes...) + + bytes = append(bytes, sp.ProofVC2.ToBytes()...) + + return bytes +} + +// ProofG1 is a proof of knowledge of a signature and hidden messages. +type ProofG1 struct { + Commitment *ml.G1 + Responses []*ml.Zr +} + +// NewProofG1 creates a new ProofG1. +func NewProofG1(commitment *ml.G1, responses []*ml.Zr) *ProofG1 { + return &ProofG1{ + Commitment: commitment, + Responses: responses, + } +} + +// Verify verifies the ProofG1. +func (pg1 *ProofG1) Verify(bases []*ml.G1, commitment *ml.G1, challenge *ml.Zr) error { + contribution := pg1.getChallengeContribution(bases, commitment, challenge) + contribution.Sub(pg1.Commitment) + + if !contribution.IsInfinity() { + return errors.New("contribution is not zero") + } + + return nil +} + +func (pg1 *ProofG1) getChallengeContribution(bases []*ml.G1, commitment *ml.G1, + challenge *ml.Zr) *ml.G1 { + points := append(bases, commitment) + scalars := append(pg1.Responses, challenge) + + return sumOfG1Products(points, scalars) +} + +// ToBytes converts ProofG1 to bytes. +func (pg1 *ProofG1) ToBytes() []byte { + bytes := make([]byte, 0) + + commitmentBytes := pg1.Commitment.Compressed() + bytes = append(bytes, commitmentBytes...) + + lenBytes := make([]byte, 4) + binary.BigEndian.PutUint32(lenBytes, uint32(len(pg1.Responses))) + bytes = append(bytes, lenBytes...) + + for i := range pg1.Responses { + responseBytes := frToRepr(pg1.Responses[i]).Bytes() + bytes = append(bytes, responseBytes...) + } + + return bytes +} + +// ParseSignatureProof parses a signature proof. +func ParseSignatureProof(sigProofBytes []byte) (*PoKOfSignatureProof, error) { + if len(sigProofBytes) < g1CompressedSize*3 { + return nil, errors.New("invalid size of signature proof") + } + + g1Points := make([]*ml.G1, 3) + offset := 0 + + for i := range g1Points { + g1Point, err := curve.NewG1FromCompressed(sigProofBytes[offset : offset+g1CompressedSize]) + if err != nil { + return nil, fmt.Errorf("parse G1 point: %w", err) + } + + g1Points[i] = g1Point + offset += g1CompressedSize + } + + proof1BytesLen := int(uint32FromBytes(sigProofBytes[offset : offset+4])) + offset += 4 + + proofVc1, err := ParseProofG1(sigProofBytes[offset : offset+proof1BytesLen]) + if err != nil { + return nil, fmt.Errorf("parse G1 proof: %w", err) + } + + offset += proof1BytesLen + + proofVc2, err := ParseProofG1(sigProofBytes[offset:]) + if err != nil { + return nil, fmt.Errorf("parse G1 proof: %w", err) + } + + return &PoKOfSignatureProof{ + aPrime: g1Points[0], + aBar: g1Points[1], + d: g1Points[2], + proofVC1: proofVc1, + ProofVC2: proofVc2, + }, nil +} + +// ParseProofG1 parses ProofG1 from bytes. +func ParseProofG1(bytes []byte) (*ProofG1, error) { + if len(bytes) < g1CompressedSize+4 { + return nil, errors.New("invalid size of G1 signature proof") + } + + offset := 0 + + commitment, err := curve.NewG1FromCompressed(bytes[:g1CompressedSize]) + if err != nil { + return nil, fmt.Errorf("parse G1 point: %w", err) + } + + offset += g1CompressedSize + length := int(uint32FromBytes(bytes[offset : offset+4])) + offset += 4 + + if len(bytes) < g1CompressedSize+4+length*frCompressedSize { + return nil, errors.New("invalid size of G1 signature proof") + } + + responses := make([]*ml.Zr, length) + for i := 0; i < length; i++ { + responses[i] = parseFr(bytes[offset : offset+frCompressedSize]) + offset += frCompressedSize + } + + return NewProofG1(commitment, responses), nil +} diff --git a/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/utils.go b/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/utils.go new file mode 100644 index 000000000..8bcb34305 --- /dev/null +++ b/vendor/github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub/utils.go @@ -0,0 +1,125 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package bbs12381g2pub + +import ( + "encoding/binary" + "errors" +) + +func uint32ToBytes(value uint32) []byte { + bytes := make([]byte, 4) + + binary.BigEndian.PutUint32(bytes, value) + + return bytes +} + +func uint16FromBytes(bytes []byte) uint16 { + return binary.BigEndian.Uint16(bytes) +} + +func uint32FromBytes(bytes []byte) uint32 { + return binary.BigEndian.Uint32(bytes) +} + +func bitvectorToIndexes(data []byte) []int { + revealedIndexes := make([]int, 0) + scalar := 0 + + for _, v := range data { + remaining := 8 + + for v > 0 { + revealed := v & 1 + if revealed == 1 { + revealedIndexes = append(revealedIndexes, scalar) + } + + v >>= 1 + scalar++ + remaining-- + } + + scalar += remaining + } + + return revealedIndexes +} + +type pokPayload struct { + messagesCount int + Revealed []int +} + +// nolint:gomnd +func ParsePoKPayload(bytes []byte) (*pokPayload, error) { + if len(bytes) < 2 { + return nil, errors.New("invalid size of PoK payload") + } + + messagesCount := int(uint16FromBytes(bytes[0:2])) + offset := lenInBytes(messagesCount) + + if len(bytes) < offset { + return nil, errors.New("invalid size of PoK payload") + } + + revealed := bitvectorToIndexes(reverseBytes(bytes[2:offset])) + + return &pokPayload{ + messagesCount: messagesCount, + Revealed: revealed, + }, nil +} + +// nolint:gomnd +func (p *pokPayload) ToBytes() ([]byte, error) { + bytes := make([]byte, p.LenInBytes()) + + binary.BigEndian.PutUint16(bytes, uint16(p.messagesCount)) + + bitvector := bytes[2:] + + for _, r := range p.Revealed { + idx := r / 8 + bit := r % 8 + + if len(bitvector) <= idx { + return nil, errors.New("invalid size of PoK payload") + } + + bitvector[idx] |= 1 << bit + } + + reverseBytes(bitvector) + + return bytes, nil +} + +func (p *pokPayload) LenInBytes() int { + return lenInBytes(p.messagesCount) +} + +func lenInBytes(messagesCount int) int { + return 2 + (messagesCount / 8) + 1 //nolint:gomnd +} + +func NewPoKPayload(messagesCount int, revealed []int) *pokPayload { + return &pokPayload{ + messagesCount: messagesCount, + Revealed: revealed, + } +} + +func reverseBytes(s []byte) []byte { + for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + + return s +} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare.go b/vendor/github.com/stretchr/testify/assert/assertion_compare.go index 95d8e59da..b774da88d 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_compare.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare.go @@ -352,9 +352,9 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { // Greater asserts that the first element is greater than the second // -// assert.Greater(t, 2, 1) -// assert.Greater(t, float64(2), float64(1)) -// assert.Greater(t, "b", "a") +// assert.Greater(t, 2, 1) +// assert.Greater(t, float64(2), float64(1)) +// assert.Greater(t, "b", "a") func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -364,10 +364,10 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface // GreaterOrEqual asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqual(t, 2, 1) -// assert.GreaterOrEqual(t, 2, 2) -// assert.GreaterOrEqual(t, "b", "a") -// assert.GreaterOrEqual(t, "b", "b") +// assert.GreaterOrEqual(t, 2, 1) +// assert.GreaterOrEqual(t, 2, 2) +// assert.GreaterOrEqual(t, "b", "a") +// assert.GreaterOrEqual(t, "b", "b") func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -377,9 +377,9 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in // Less asserts that the first element is less than the second // -// assert.Less(t, 1, 2) -// assert.Less(t, float64(1), float64(2)) -// assert.Less(t, "a", "b") +// assert.Less(t, 1, 2) +// assert.Less(t, float64(1), float64(2)) +// assert.Less(t, "a", "b") func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -389,10 +389,10 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) // LessOrEqual asserts that the first element is less than or equal to the second // -// assert.LessOrEqual(t, 1, 2) -// assert.LessOrEqual(t, 2, 2) -// assert.LessOrEqual(t, "a", "b") -// assert.LessOrEqual(t, "b", "b") +// assert.LessOrEqual(t, 1, 2) +// assert.LessOrEqual(t, 2, 2) +// assert.LessOrEqual(t, "a", "b") +// assert.LessOrEqual(t, "b", "b") func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -402,8 +402,8 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter // Positive asserts that the specified element is positive // -// assert.Positive(t, 1) -// assert.Positive(t, 1.23) +// assert.Positive(t, 1) +// assert.Positive(t, 1.23) func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -414,8 +414,8 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { // Negative asserts that the specified element is negative // -// assert.Negative(t, -1) -// assert.Negative(t, -1.23) +// assert.Negative(t, -1) +// assert.Negative(t, -1.23) func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index 7880b8f94..84dbd6c79 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -22,9 +22,9 @@ func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bo // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") -// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") -// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") +// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") +// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") +// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -56,7 +56,7 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// assert.Emptyf(t, obj, "error message %s", "formatted") +// assert.Emptyf(t, obj, "error message %s", "formatted") func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -66,7 +66,7 @@ func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) boo // Equalf asserts that two objects are equal. // -// assert.Equalf(t, 123, 123, "error message %s", "formatted") +// assert.Equalf(t, 123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -81,8 +81,8 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -90,10 +90,27 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...) } +// EqualExportedValuesf asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// assert.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// assert.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return EqualExportedValues(t, expected, actual, append([]interface{}{msg}, args...)...) +} + // EqualValuesf asserts that two objects are equal or convertable to the same types // and equal. // -// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") +// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -103,10 +120,10 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Errorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// if assert.Errorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -126,8 +143,8 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -147,7 +164,7 @@ func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -155,9 +172,34 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick return Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) } +// EventuallyWithTf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithTf(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return EventuallyWithT(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) +} + // Exactlyf asserts that two objects are equal in value and type. // -// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") +// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -183,7 +225,7 @@ func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{} // Falsef asserts that the specified value is false. // -// assert.Falsef(t, myBool, "error message %s", "formatted") +// assert.Falsef(t, myBool, "error message %s", "formatted") func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -202,9 +244,9 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool // Greaterf asserts that the first element is greater than the second // -// assert.Greaterf(t, 2, 1, "error message %s", "formatted") -// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") -// assert.Greaterf(t, "b", "a", "error message %s", "formatted") +// assert.Greaterf(t, 2, 1, "error message %s", "formatted") +// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") +// assert.Greaterf(t, "b", "a", "error message %s", "formatted") func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -214,10 +256,10 @@ func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...in // GreaterOrEqualf asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -228,7 +270,7 @@ func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, arg // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { @@ -241,7 +283,7 @@ func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { @@ -253,7 +295,7 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u // HTTPErrorf asserts that a specified handler returns an error status code. // -// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -265,7 +307,7 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, // HTTPRedirectf asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -277,7 +319,7 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri // HTTPStatusCodef asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { @@ -289,7 +331,7 @@ func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url st // HTTPSuccessf asserts that a specified handler returns a success status code. // -// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -301,7 +343,7 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin // Implementsf asserts that an object is implemented by the specified interface. // -// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -311,7 +353,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms // InDeltaf asserts that the two numerals are within delta of each other. // -// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -353,9 +395,9 @@ func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsil // IsDecreasingf asserts that the collection is decreasing // -// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -365,9 +407,9 @@ func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface // IsIncreasingf asserts that the collection is increasing // -// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -377,9 +419,9 @@ func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface // IsNonDecreasingf asserts that the collection is not decreasing // -// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -389,9 +431,9 @@ func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interf // IsNonIncreasingf asserts that the collection is not increasing // -// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -409,7 +451,7 @@ func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg strin // JSONEqf asserts that two JSON strings are equivalent. // -// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -420,7 +462,7 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // -// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") +// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -430,9 +472,9 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf // Lessf asserts that the first element is less than the second // -// assert.Lessf(t, 1, 2, "error message %s", "formatted") -// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") -// assert.Lessf(t, "a", "b", "error message %s", "formatted") +// assert.Lessf(t, 1, 2, "error message %s", "formatted") +// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") +// assert.Lessf(t, "a", "b", "error message %s", "formatted") func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -442,10 +484,10 @@ func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...inter // LessOrEqualf asserts that the first element is less than or equal to the second // -// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") -// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -455,8 +497,8 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args . // Negativef asserts that the specified element is negative // -// assert.Negativef(t, -1, "error message %s", "formatted") -// assert.Negativef(t, -1.23, "error message %s", "formatted") +// assert.Negativef(t, -1, "error message %s", "formatted") +// assert.Negativef(t, -1.23, "error message %s", "formatted") func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -467,7 +509,7 @@ func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -477,7 +519,7 @@ func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time. // Nilf asserts that the specified object is nil. // -// assert.Nilf(t, err, "error message %s", "formatted") +// assert.Nilf(t, err, "error message %s", "formatted") func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -496,10 +538,10 @@ func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) bool // NoErrorf asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if assert.NoErrorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if assert.NoErrorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -519,9 +561,9 @@ func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) boo // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -532,9 +574,9 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } +// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -544,7 +586,7 @@ func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) // NotEqualf asserts that the specified values are NOT equal. // -// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") +// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -557,7 +599,7 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") +// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -576,7 +618,7 @@ func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interf // NotNilf asserts that the specified object is not nil. // -// assert.NotNilf(t, err, "error message %s", "formatted") +// assert.NotNilf(t, err, "error message %s", "formatted") func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -586,7 +628,7 @@ func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bo // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") +// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -596,8 +638,8 @@ func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bo // NotRegexpf asserts that a specified regexp does not match a string. // -// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") +// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -607,7 +649,7 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args .. // NotSamef asserts that two pointers do not reference the same object. // -// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") +// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -621,7 +663,7 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, // NotSubsetf asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -639,7 +681,7 @@ func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { // Panicsf asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") +// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -651,7 +693,7 @@ func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -662,7 +704,7 @@ func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -672,8 +714,8 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str // Positivef asserts that the specified element is positive // -// assert.Positivef(t, 1, "error message %s", "formatted") -// assert.Positivef(t, 1.23, "error message %s", "formatted") +// assert.Positivef(t, 1, "error message %s", "formatted") +// assert.Positivef(t, 1.23, "error message %s", "formatted") func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -683,8 +725,8 @@ func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool // Regexpf asserts that a specified regexp matches a string. // -// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") +// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -694,7 +736,7 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in // Samef asserts that two pointers reference the same object. // -// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") +// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -708,7 +750,7 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg // Subsetf asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -718,7 +760,7 @@ func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args // Truef asserts that the specified value is true. // -// assert.Truef(t, myBool, "error message %s", "formatted") +// assert.Truef(t, myBool, "error message %s", "formatted") func Truef(t TestingT, value bool, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -728,7 +770,7 @@ func Truef(t TestingT, value bool, msg string, args ...interface{}) bool { // WithinDurationf asserts that the two times are within duration delta of each other. // -// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -738,7 +780,7 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim // WithinRangef asserts that a time is within a time range (inclusive). // -// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index 339515b8b..b1d94aec5 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -30,9 +30,9 @@ func (a *Assertions) Conditionf(comp Comparison, msg string, args ...interface{} // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// a.Contains("Hello World", "World") -// a.Contains(["Hello", "World"], "World") -// a.Contains({"Hello": "World"}, "Hello") +// a.Contains("Hello World", "World") +// a.Contains(["Hello", "World"], "World") +// a.Contains({"Hello": "World"}, "Hello") func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -43,9 +43,9 @@ func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs .. // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// a.Containsf("Hello World", "World", "error message %s", "formatted") -// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") -// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") +// a.Containsf("Hello World", "World", "error message %s", "formatted") +// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") +// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -98,7 +98,7 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// a.Empty(obj) +// a.Empty(obj) func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -109,7 +109,7 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// a.Emptyf(obj, "error message %s", "formatted") +// a.Emptyf(obj, "error message %s", "formatted") func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -119,7 +119,7 @@ func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) // Equal asserts that two objects are equal. // -// a.Equal(123, 123) +// a.Equal(123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -134,8 +134,8 @@ func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// a.EqualError(err, expectedErrorString) +// actualObj, err := SomeFunction() +// a.EqualError(err, expectedErrorString) func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -146,8 +146,8 @@ func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ... // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -155,10 +155,44 @@ func (a *Assertions) EqualErrorf(theError error, errString string, msg string, a return EqualErrorf(a.t, theError, errString, msg, args...) } +// EqualExportedValues asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// a.EqualExportedValues(S{1, 2}, S{1, 3}) => true +// a.EqualExportedValues(S{1, 2}, S{2, 3}) => false +func (a *Assertions) EqualExportedValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EqualExportedValues(a.t, expected, actual, msgAndArgs...) +} + +// EqualExportedValuesf asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// a.EqualExportedValuesf(S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// a.EqualExportedValuesf(S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EqualExportedValuesf(a.t, expected, actual, msg, args...) +} + // EqualValues asserts that two objects are equal or convertable to the same types // and equal. // -// a.EqualValues(uint32(123), int32(123)) +// a.EqualValues(uint32(123), int32(123)) func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -169,7 +203,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn // EqualValuesf asserts that two objects are equal or convertable to the same types // and equal. // -// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") +// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -179,7 +213,7 @@ func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg // Equalf asserts that two objects are equal. // -// a.Equalf(123, 123, "error message %s", "formatted") +// a.Equalf(123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -193,10 +227,10 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Error(err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// if a.Error(err) { +// assert.Equal(t, expectedError, err) +// } func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -225,8 +259,8 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args .. // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// a.ErrorContains(err, expectedErrorSubString) +// actualObj, err := SomeFunction() +// a.ErrorContains(err, expectedErrorSubString) func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -237,8 +271,8 @@ func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs . // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -266,10 +300,10 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Errorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// if a.Errorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -280,7 +314,7 @@ func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) +// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -288,10 +322,60 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti return Eventually(a.t, condition, waitFor, tick, msgAndArgs...) } +// EventuallyWithT asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithT(func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// EventuallyWithTf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithTf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EventuallyWithTf(a.t, condition, waitFor, tick, msg, args...) +} + // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -301,7 +385,7 @@ func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, t // Exactly asserts that two objects are equal in value and type. // -// a.Exactly(int32(123), int64(123)) +// a.Exactly(int32(123), int64(123)) func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -311,7 +395,7 @@ func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArg // Exactlyf asserts that two objects are equal in value and type. // -// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") +// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -353,7 +437,7 @@ func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{ // False asserts that the specified value is false. // -// a.False(myBool) +// a.False(myBool) func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -363,7 +447,7 @@ func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { // Falsef asserts that the specified value is false. // -// a.Falsef(myBool, "error message %s", "formatted") +// a.Falsef(myBool, "error message %s", "formatted") func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -391,9 +475,9 @@ func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) b // Greater asserts that the first element is greater than the second // -// a.Greater(2, 1) -// a.Greater(float64(2), float64(1)) -// a.Greater("b", "a") +// a.Greater(2, 1) +// a.Greater(float64(2), float64(1)) +// a.Greater("b", "a") func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -403,10 +487,10 @@ func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...inter // GreaterOrEqual asserts that the first element is greater than or equal to the second // -// a.GreaterOrEqual(2, 1) -// a.GreaterOrEqual(2, 2) -// a.GreaterOrEqual("b", "a") -// a.GreaterOrEqual("b", "b") +// a.GreaterOrEqual(2, 1) +// a.GreaterOrEqual(2, 2) +// a.GreaterOrEqual("b", "a") +// a.GreaterOrEqual("b", "b") func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -416,10 +500,10 @@ func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs . // GreaterOrEqualf asserts that the first element is greater than or equal to the second // -// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") -// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") -// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") -// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") +// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") +// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") +// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") +// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -429,9 +513,9 @@ func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, // Greaterf asserts that the first element is greater than the second // -// a.Greaterf(2, 1, "error message %s", "formatted") -// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") -// a.Greaterf("b", "a", "error message %s", "formatted") +// a.Greaterf(2, 1, "error message %s", "formatted") +// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") +// a.Greaterf("b", "a", "error message %s", "formatted") func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -442,7 +526,7 @@ func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args . // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { @@ -455,7 +539,7 @@ func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, u // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { @@ -468,7 +552,7 @@ func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { @@ -481,7 +565,7 @@ func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { @@ -493,7 +577,7 @@ func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method strin // HTTPError asserts that a specified handler returns an error status code. // -// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -505,7 +589,7 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri // HTTPErrorf asserts that a specified handler returns an error status code. // -// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -517,7 +601,7 @@ func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url str // HTTPRedirect asserts that a specified handler returns a redirect status code. // -// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -529,7 +613,7 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s // HTTPRedirectf asserts that a specified handler returns a redirect status code. // -// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -541,7 +625,7 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url // HTTPStatusCode asserts that a specified handler returns a specified status code. // -// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) +// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { @@ -553,7 +637,7 @@ func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url // HTTPStatusCodef asserts that a specified handler returns a specified status code. // -// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { @@ -565,7 +649,7 @@ func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, ur // HTTPSuccess asserts that a specified handler returns a success status code. // -// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) +// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -577,7 +661,7 @@ func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url st // HTTPSuccessf asserts that a specified handler returns a success status code. // -// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -589,7 +673,7 @@ func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url s // Implements asserts that an object is implemented by the specified interface. // -// a.Implements((*MyInterface)(nil), new(MyObject)) +// a.Implements((*MyInterface)(nil), new(MyObject)) func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -599,7 +683,7 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, // Implementsf asserts that an object is implemented by the specified interface. // -// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -609,7 +693,7 @@ func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{} // InDelta asserts that the two numerals are within delta of each other. // -// a.InDelta(math.Pi, 22/7.0, 0.01) +// a.InDelta(math.Pi, 22/7.0, 0.01) func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -651,7 +735,7 @@ func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, del // InDeltaf asserts that the two numerals are within delta of each other. // -// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -693,9 +777,9 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo // IsDecreasing asserts that the collection is decreasing // -// a.IsDecreasing([]int{2, 1, 0}) -// a.IsDecreasing([]float{2, 1}) -// a.IsDecreasing([]string{"b", "a"}) +// a.IsDecreasing([]int{2, 1, 0}) +// a.IsDecreasing([]float{2, 1}) +// a.IsDecreasing([]string{"b", "a"}) func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -705,9 +789,9 @@ func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) // IsDecreasingf asserts that the collection is decreasing // -// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") -// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") +// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") +// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -717,9 +801,9 @@ func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...inter // IsIncreasing asserts that the collection is increasing // -// a.IsIncreasing([]int{1, 2, 3}) -// a.IsIncreasing([]float{1, 2}) -// a.IsIncreasing([]string{"a", "b"}) +// a.IsIncreasing([]int{1, 2, 3}) +// a.IsIncreasing([]float{1, 2}) +// a.IsIncreasing([]string{"a", "b"}) func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -729,9 +813,9 @@ func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) // IsIncreasingf asserts that the collection is increasing // -// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") -// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") +// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") +// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -741,9 +825,9 @@ func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...inter // IsNonDecreasing asserts that the collection is not decreasing // -// a.IsNonDecreasing([]int{1, 1, 2}) -// a.IsNonDecreasing([]float{1, 2}) -// a.IsNonDecreasing([]string{"a", "b"}) +// a.IsNonDecreasing([]int{1, 1, 2}) +// a.IsNonDecreasing([]float{1, 2}) +// a.IsNonDecreasing([]string{"a", "b"}) func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -753,9 +837,9 @@ func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface // IsNonDecreasingf asserts that the collection is not decreasing // -// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") +// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -765,9 +849,9 @@ func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...in // IsNonIncreasing asserts that the collection is not increasing // -// a.IsNonIncreasing([]int{2, 1, 1}) -// a.IsNonIncreasing([]float{2, 1}) -// a.IsNonIncreasing([]string{"b", "a"}) +// a.IsNonIncreasing([]int{2, 1, 1}) +// a.IsNonIncreasing([]float{2, 1}) +// a.IsNonIncreasing([]string{"b", "a"}) func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -777,9 +861,9 @@ func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface // IsNonIncreasingf asserts that the collection is not increasing // -// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") +// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -805,7 +889,7 @@ func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg s // JSONEq asserts that two JSON strings are equivalent. // -// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -815,7 +899,7 @@ func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interf // JSONEqf asserts that two JSON strings are equivalent. // -// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -826,7 +910,7 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args .. // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // -// a.Len(mySlice, 3) +// a.Len(mySlice, 3) func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -837,7 +921,7 @@ func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // -// a.Lenf(mySlice, 3, "error message %s", "formatted") +// a.Lenf(mySlice, 3, "error message %s", "formatted") func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -847,9 +931,9 @@ func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...in // Less asserts that the first element is less than the second // -// a.Less(1, 2) -// a.Less(float64(1), float64(2)) -// a.Less("a", "b") +// a.Less(1, 2) +// a.Less(float64(1), float64(2)) +// a.Less("a", "b") func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -859,10 +943,10 @@ func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interfac // LessOrEqual asserts that the first element is less than or equal to the second // -// a.LessOrEqual(1, 2) -// a.LessOrEqual(2, 2) -// a.LessOrEqual("a", "b") -// a.LessOrEqual("b", "b") +// a.LessOrEqual(1, 2) +// a.LessOrEqual(2, 2) +// a.LessOrEqual("a", "b") +// a.LessOrEqual("b", "b") func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -872,10 +956,10 @@ func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...i // LessOrEqualf asserts that the first element is less than or equal to the second // -// a.LessOrEqualf(1, 2, "error message %s", "formatted") -// a.LessOrEqualf(2, 2, "error message %s", "formatted") -// a.LessOrEqualf("a", "b", "error message %s", "formatted") -// a.LessOrEqualf("b", "b", "error message %s", "formatted") +// a.LessOrEqualf(1, 2, "error message %s", "formatted") +// a.LessOrEqualf(2, 2, "error message %s", "formatted") +// a.LessOrEqualf("a", "b", "error message %s", "formatted") +// a.LessOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -885,9 +969,9 @@ func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, ar // Lessf asserts that the first element is less than the second // -// a.Lessf(1, 2, "error message %s", "formatted") -// a.Lessf(float64(1), float64(2), "error message %s", "formatted") -// a.Lessf("a", "b", "error message %s", "formatted") +// a.Lessf(1, 2, "error message %s", "formatted") +// a.Lessf(float64(1), float64(2), "error message %s", "formatted") +// a.Lessf("a", "b", "error message %s", "formatted") func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -897,8 +981,8 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i // Negative asserts that the specified element is negative // -// a.Negative(-1) -// a.Negative(-1.23) +// a.Negative(-1) +// a.Negative(-1.23) func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -908,8 +992,8 @@ func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool { // Negativef asserts that the specified element is negative // -// a.Negativef(-1, "error message %s", "formatted") -// a.Negativef(-1.23, "error message %s", "formatted") +// a.Negativef(-1, "error message %s", "formatted") +// a.Negativef(-1.23, "error message %s", "formatted") func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -920,7 +1004,7 @@ func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) b // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) +// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -931,7 +1015,7 @@ func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick ti // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -941,7 +1025,7 @@ func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick t // Nil asserts that the specified object is nil. // -// a.Nil(err) +// a.Nil(err) func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -951,7 +1035,7 @@ func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { // Nilf asserts that the specified object is nil. // -// a.Nilf(err, "error message %s", "formatted") +// a.Nilf(err, "error message %s", "formatted") func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -979,10 +1063,10 @@ func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) // NoError asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if a.NoError(err) { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if a.NoError(err) { +// assert.Equal(t, expectedObj, actualObj) +// } func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -992,10 +1076,10 @@ func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { // NoErrorf asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if a.NoErrorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if a.NoErrorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1024,9 +1108,9 @@ func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// a.NotContains("Hello World", "Earth") -// a.NotContains(["Hello", "World"], "Earth") -// a.NotContains({"Hello": "World"}, "Earth") +// a.NotContains("Hello World", "Earth") +// a.NotContains(["Hello", "World"], "Earth") +// a.NotContains({"Hello": "World"}, "Earth") func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1037,9 +1121,9 @@ func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") -// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") -// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") +// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") +// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") +// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1050,9 +1134,9 @@ func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg strin // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if a.NotEmpty(obj) { -// assert.Equal(t, "two", obj[1]) -// } +// if a.NotEmpty(obj) { +// assert.Equal(t, "two", obj[1]) +// } func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1063,9 +1147,9 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) boo // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if a.NotEmptyf(obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } +// if a.NotEmptyf(obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1075,7 +1159,7 @@ func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface // NotEqual asserts that the specified values are NOT equal. // -// a.NotEqual(obj1, obj2) +// a.NotEqual(obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1088,7 +1172,7 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr // NotEqualValues asserts that two objects are not equal even when converted to the same type // -// a.NotEqualValues(obj1, obj2) +// a.NotEqualValues(obj1, obj2) func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1098,7 +1182,7 @@ func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, ms // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // -// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") +// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1108,7 +1192,7 @@ func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, m // NotEqualf asserts that the specified values are NOT equal. // -// a.NotEqualf(obj1, obj2, "error message %s", "formatted") +// a.NotEqualf(obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1139,7 +1223,7 @@ func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...in // NotNil asserts that the specified object is not nil. // -// a.NotNil(err) +// a.NotNil(err) func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1149,7 +1233,7 @@ func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool // NotNilf asserts that the specified object is not nil. // -// a.NotNilf(err, "error message %s", "formatted") +// a.NotNilf(err, "error message %s", "formatted") func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1159,7 +1243,7 @@ func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{} // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // -// a.NotPanics(func(){ RemainCalm() }) +// a.NotPanics(func(){ RemainCalm() }) func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1169,7 +1253,7 @@ func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // -// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") +// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1179,8 +1263,8 @@ func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{} // NotRegexp asserts that a specified regexp does not match a string. // -// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") -// a.NotRegexp("^start", "it's not starting") +// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") +// a.NotRegexp("^start", "it's not starting") func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1190,8 +1274,8 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in // NotRegexpf asserts that a specified regexp does not match a string. // -// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") +// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1201,7 +1285,7 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg // NotSame asserts that two pointers do not reference the same object. // -// a.NotSame(ptr1, ptr2) +// a.NotSame(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1214,7 +1298,7 @@ func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArg // NotSamef asserts that two pointers do not reference the same object. // -// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") +// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1228,7 +1312,7 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri // NotSubset asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1239,7 +1323,7 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs // NotSubsetf asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1265,7 +1349,7 @@ func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) bo // Panics asserts that the code inside the specified PanicTestFunc panics. // -// a.Panics(func(){ GoCrazy() }) +// a.Panics(func(){ GoCrazy() }) func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1277,7 +1361,7 @@ func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// a.PanicsWithError("crazy error", func(){ GoCrazy() }) +// a.PanicsWithError("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithError(errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1289,7 +1373,7 @@ func (a *Assertions) PanicsWithError(errString string, f PanicTestFunc, msgAndAr // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithErrorf(errString string, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1300,7 +1384,7 @@ func (a *Assertions) PanicsWithErrorf(errString string, f PanicTestFunc, msg str // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) +// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1311,7 +1395,7 @@ func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgA // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1321,7 +1405,7 @@ func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg // Panicsf asserts that the code inside the specified PanicTestFunc panics. // -// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") +// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1331,8 +1415,8 @@ func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) b // Positive asserts that the specified element is positive // -// a.Positive(1) -// a.Positive(1.23) +// a.Positive(1) +// a.Positive(1.23) func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1342,8 +1426,8 @@ func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool { // Positivef asserts that the specified element is positive // -// a.Positivef(1, "error message %s", "formatted") -// a.Positivef(1.23, "error message %s", "formatted") +// a.Positivef(1, "error message %s", "formatted") +// a.Positivef(1.23, "error message %s", "formatted") func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1353,8 +1437,8 @@ func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) b // Regexp asserts that a specified regexp matches a string. // -// a.Regexp(regexp.MustCompile("start"), "it's starting") -// a.Regexp("start...$", "it's not starting") +// a.Regexp(regexp.MustCompile("start"), "it's starting") +// a.Regexp("start...$", "it's not starting") func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1364,8 +1448,8 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter // Regexpf asserts that a specified regexp matches a string. // -// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") +// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1375,7 +1459,7 @@ func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args . // Same asserts that two pointers reference the same object. // -// a.Same(ptr1, ptr2) +// a.Same(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1388,7 +1472,7 @@ func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs . // Samef asserts that two pointers reference the same object. // -// a.Samef(ptr1, ptr2, "error message %s", "formatted") +// a.Samef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1402,7 +1486,7 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1413,7 +1497,7 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... // Subsetf asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1423,7 +1507,7 @@ func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, a // True asserts that the specified value is true. // -// a.True(myBool) +// a.True(myBool) func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1433,7 +1517,7 @@ func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { // Truef asserts that the specified value is true. // -// a.Truef(myBool, "error message %s", "formatted") +// a.Truef(myBool, "error message %s", "formatted") func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1443,7 +1527,7 @@ func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool { // WithinDuration asserts that the two times are within duration delta of each other. // -// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) +// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1453,7 +1537,7 @@ func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta // WithinDurationf asserts that the two times are within duration delta of each other. // -// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1463,7 +1547,7 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta // WithinRange asserts that a time is within a time range (inclusive). // -// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1473,7 +1557,7 @@ func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Tim // WithinRangef asserts that a time is within a time range (inclusive). // -// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_order.go b/vendor/github.com/stretchr/testify/assert/assertion_order.go index 759448783..00df62a05 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_order.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_order.go @@ -46,36 +46,36 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareT // IsIncreasing asserts that the collection is increasing // -// assert.IsIncreasing(t, []int{1, 2, 3}) -// assert.IsIncreasing(t, []float{1, 2}) -// assert.IsIncreasing(t, []string{"a", "b"}) +// assert.IsIncreasing(t, []int{1, 2, 3}) +// assert.IsIncreasing(t, []float{1, 2}) +// assert.IsIncreasing(t, []string{"a", "b"}) func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) } // IsNonIncreasing asserts that the collection is not increasing // -// assert.IsNonIncreasing(t, []int{2, 1, 1}) -// assert.IsNonIncreasing(t, []float{2, 1}) -// assert.IsNonIncreasing(t, []string{"b", "a"}) +// assert.IsNonIncreasing(t, []int{2, 1, 1}) +// assert.IsNonIncreasing(t, []float{2, 1}) +// assert.IsNonIncreasing(t, []string{"b", "a"}) func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) } // IsDecreasing asserts that the collection is decreasing // -// assert.IsDecreasing(t, []int{2, 1, 0}) -// assert.IsDecreasing(t, []float{2, 1}) -// assert.IsDecreasing(t, []string{"b", "a"}) +// assert.IsDecreasing(t, []int{2, 1, 0}) +// assert.IsDecreasing(t, []float{2, 1}) +// assert.IsDecreasing(t, []string{"b", "a"}) func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) } // IsNonDecreasing asserts that the collection is not decreasing // -// assert.IsNonDecreasing(t, []int{1, 1, 2}) -// assert.IsNonDecreasing(t, []float{1, 2}) -// assert.IsNonDecreasing(t, []string{"a", "b"}) +// assert.IsNonDecreasing(t, []int{1, 1, 2}) +// assert.IsNonDecreasing(t, []float{1, 2}) +// assert.IsNonDecreasing(t, []string{"a", "b"}) func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) } diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index 2924cf3a1..a55d1bba9 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -75,6 +75,77 @@ func ObjectsAreEqual(expected, actual interface{}) bool { return bytes.Equal(exp, act) } +// copyExportedFields iterates downward through nested data structures and creates a copy +// that only contains the exported struct fields. +func copyExportedFields(expected interface{}) interface{} { + if isNil(expected) { + return expected + } + + expectedType := reflect.TypeOf(expected) + expectedKind := expectedType.Kind() + expectedValue := reflect.ValueOf(expected) + + switch expectedKind { + case reflect.Struct: + result := reflect.New(expectedType).Elem() + for i := 0; i < expectedType.NumField(); i++ { + field := expectedType.Field(i) + isExported := field.IsExported() + if isExported { + fieldValue := expectedValue.Field(i) + if isNil(fieldValue) || isNil(fieldValue.Interface()) { + continue + } + newValue := copyExportedFields(fieldValue.Interface()) + result.Field(i).Set(reflect.ValueOf(newValue)) + } + } + return result.Interface() + + case reflect.Ptr: + result := reflect.New(expectedType.Elem()) + unexportedRemoved := copyExportedFields(expectedValue.Elem().Interface()) + result.Elem().Set(reflect.ValueOf(unexportedRemoved)) + return result.Interface() + + case reflect.Array, reflect.Slice: + result := reflect.MakeSlice(expectedType, expectedValue.Len(), expectedValue.Len()) + for i := 0; i < expectedValue.Len(); i++ { + index := expectedValue.Index(i) + if isNil(index) { + continue + } + unexportedRemoved := copyExportedFields(index.Interface()) + result.Index(i).Set(reflect.ValueOf(unexportedRemoved)) + } + return result.Interface() + + case reflect.Map: + result := reflect.MakeMap(expectedType) + for _, k := range expectedValue.MapKeys() { + index := expectedValue.MapIndex(k) + unexportedRemoved := copyExportedFields(index.Interface()) + result.SetMapIndex(k, reflect.ValueOf(unexportedRemoved)) + } + return result.Interface() + + default: + return expected + } +} + +// ObjectsExportedFieldsAreEqual determines if the exported (public) fields of two objects are +// considered equal. This comparison of only exported fields is applied recursively to nested data +// structures. +// +// This function does no assertion of any kind. +func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool { + expectedCleaned := copyExportedFields(expected) + actualCleaned := copyExportedFields(actual) + return ObjectsAreEqualValues(expectedCleaned, actualCleaned) +} + // ObjectsAreEqualValues gets whether two objects are equal, or if their // values are equal. func ObjectsAreEqualValues(expected, actual interface{}) bool { @@ -271,7 +342,7 @@ type labeledContent struct { // labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner: // -// \t{{label}}:{{align_spaces}}\t{{content}}\n +// \t{{label}}:{{align_spaces}}\t{{content}}\n // // The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label. // If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this @@ -294,7 +365,7 @@ func labeledOutput(content ...labeledContent) string { // Implements asserts that an object is implemented by the specified interface. // -// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) +// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -326,7 +397,7 @@ func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs // Equal asserts that two objects are equal. // -// assert.Equal(t, 123, 123) +// assert.Equal(t, 123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -367,7 +438,7 @@ func validateEqualArgs(expected, actual interface{}) error { // Same asserts that two pointers reference the same object. // -// assert.Same(t, ptr1, ptr2) +// assert.Same(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -387,7 +458,7 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b // NotSame asserts that two pointers do not reference the same object. // -// assert.NotSame(t, ptr1, ptr2) +// assert.NotSame(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -455,7 +526,7 @@ func truncatingFormat(data interface{}) string { // EqualValues asserts that two objects are equal or convertable to the same types // and equal. // -// assert.EqualValues(t, uint32(123), int32(123)) +// assert.EqualValues(t, uint32(123), int32(123)) func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -473,9 +544,53 @@ func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interfa } +// EqualExportedValues asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true +// assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false +func EqualExportedValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + aType := reflect.TypeOf(expected) + bType := reflect.TypeOf(actual) + + if aType != bType { + return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...) + } + + if aType.Kind() != reflect.Struct { + return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", aType.Kind(), reflect.Struct), msgAndArgs...) + } + + if bType.Kind() != reflect.Struct { + return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", bType.Kind(), reflect.Struct), msgAndArgs...) + } + + expected = copyExportedFields(expected) + actual = copyExportedFields(actual) + + if !ObjectsAreEqualValues(expected, actual) { + diff := diff(expected, actual) + expected, actual = formatUnequalValues(expected, actual) + return Fail(t, fmt.Sprintf("Not equal (comparing only exported fields): \n"+ + "expected: %s\n"+ + "actual : %s%s", expected, actual, diff), msgAndArgs...) + } + + return true +} + // Exactly asserts that two objects are equal in value and type. // -// assert.Exactly(t, int32(123), int64(123)) +// assert.Exactly(t, int32(123), int64(123)) func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -494,7 +609,7 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} // NotNil asserts that the specified object is not nil. // -// assert.NotNil(t, err) +// assert.NotNil(t, err) func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { if !isNil(object) { return true @@ -540,7 +655,7 @@ func isNil(object interface{}) bool { // Nil asserts that the specified object is nil. // -// assert.Nil(t, err) +// assert.Nil(t, err) func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { if isNil(object) { return true @@ -583,7 +698,7 @@ func isEmpty(object interface{}) bool { // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// assert.Empty(t, obj) +// assert.Empty(t, obj) func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { pass := isEmpty(object) if !pass { @@ -600,9 +715,9 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if assert.NotEmpty(t, obj) { -// assert.Equal(t, "two", obj[1]) -// } +// if assert.NotEmpty(t, obj) { +// assert.Equal(t, "two", obj[1]) +// } func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { pass := !isEmpty(object) if !pass { @@ -631,7 +746,7 @@ func getLen(x interface{}) (ok bool, length int) { // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // -// assert.Len(t, mySlice, 3) +// assert.Len(t, mySlice, 3) func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -649,7 +764,7 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) // True asserts that the specified value is true. // -// assert.True(t, myBool) +// assert.True(t, myBool) func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { if !value { if h, ok := t.(tHelper); ok { @@ -664,7 +779,7 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { // False asserts that the specified value is false. // -// assert.False(t, myBool) +// assert.False(t, myBool) func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { if value { if h, ok := t.(tHelper); ok { @@ -679,7 +794,7 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { // NotEqual asserts that the specified values are NOT equal. // -// assert.NotEqual(t, obj1, obj2) +// assert.NotEqual(t, obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -702,7 +817,7 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{ // NotEqualValues asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValues(t, obj1, obj2) +// assert.NotEqualValues(t, obj1, obj2) func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -761,9 +876,9 @@ func containsElement(list interface{}, element interface{}) (ok, found bool) { // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Contains(t, "Hello World", "World") -// assert.Contains(t, ["Hello", "World"], "World") -// assert.Contains(t, {"Hello": "World"}, "Hello") +// assert.Contains(t, "Hello World", "World") +// assert.Contains(t, ["Hello", "World"], "World") +// assert.Contains(t, {"Hello": "World"}, "Hello") func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -784,9 +899,9 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContains(t, "Hello World", "Earth") -// assert.NotContains(t, ["Hello", "World"], "Earth") -// assert.NotContains(t, {"Hello": "World"}, "Earth") +// assert.NotContains(t, "Hello World", "Earth") +// assert.NotContains(t, ["Hello", "World"], "Earth") +// assert.NotContains(t, {"Hello": "World"}, "Earth") func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -794,10 +909,10 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) ok, found := containsElement(s, contains) if !ok { - return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) } if found { - return Fail(t, fmt.Sprintf("\"%s\" should not contain \"%s\"", s, contains), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v should not contain %#v", s, contains), msgAndArgs...) } return true @@ -807,7 +922,7 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() @@ -863,7 +978,7 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok // NotSubset asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1048,7 +1163,7 @@ func didPanic(f PanicTestFunc) (didPanic bool, message interface{}, stack string // Panics asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panics(t, func(){ GoCrazy() }) +// assert.Panics(t, func(){ GoCrazy() }) func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1064,7 +1179,7 @@ func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) +// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1085,7 +1200,7 @@ func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndAr // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) +// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1105,7 +1220,7 @@ func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs . // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanics(t, func(){ RemainCalm() }) +// assert.NotPanics(t, func(){ RemainCalm() }) func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1120,7 +1235,7 @@ func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { // WithinDuration asserts that the two times are within duration delta of each other. // -// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) +// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1136,7 +1251,7 @@ func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, // WithinRange asserts that a time is within a time range (inclusive). // -// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func WithinRange(t TestingT, actual, start, end time.Time, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1195,7 +1310,7 @@ func toFloat(x interface{}) (float64, bool) { // InDelta asserts that the two numerals are within delta of each other. // -// assert.InDelta(t, math.Pi, 22/7.0, 0.01) +// assert.InDelta(t, math.Pi, 22/7.0, 0.01) func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1368,10 +1483,10 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m // NoError asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if assert.NoError(t, err) { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if assert.NoError(t, err) { +// assert.Equal(t, expectedObj, actualObj) +// } func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { if err != nil { if h, ok := t.(tHelper); ok { @@ -1385,10 +1500,10 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Error(t, err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// if assert.Error(t, err) { +// assert.Equal(t, expectedError, err) +// } func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { if err == nil { if h, ok := t.(tHelper); ok { @@ -1403,8 +1518,8 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// assert.EqualError(t, err, expectedErrorString) +// actualObj, err := SomeFunction() +// assert.EqualError(t, err, expectedErrorString) func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1426,8 +1541,8 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// assert.ErrorContains(t, err, expectedErrorSubString) +// actualObj, err := SomeFunction() +// assert.ErrorContains(t, err, expectedErrorSubString) func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1460,8 +1575,8 @@ func matchRegexp(rx interface{}, str interface{}) bool { // Regexp asserts that a specified regexp matches a string. // -// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") -// assert.Regexp(t, "start...$", "it's not starting") +// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") +// assert.Regexp(t, "start...$", "it's not starting") func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1478,8 +1593,8 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface // NotRegexp asserts that a specified regexp does not match a string. // -// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") -// assert.NotRegexp(t, "^start", "it's not starting") +// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// assert.NotRegexp(t, "^start", "it's not starting") func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1591,7 +1706,7 @@ func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { // JSONEq asserts that two JSON strings are equivalent. // -// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1714,7 +1829,7 @@ type tHelper interface { // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) +// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1744,10 +1859,93 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t } } +// CollectT implements the TestingT interface and collects all errors. +type CollectT struct { + errors []error +} + +// Errorf collects the error. +func (c *CollectT) Errorf(format string, args ...interface{}) { + c.errors = append(c.errors, fmt.Errorf(format, args...)) +} + +// FailNow panics. +func (c *CollectT) FailNow() { + panic("Assertion failed") +} + +// Reset clears the collected errors. +func (c *CollectT) Reset() { + c.errors = nil +} + +// Copy copies the collected errors to the supplied t. +func (c *CollectT) Copy(t TestingT) { + if tt, ok := t.(tHelper); ok { + tt.Helper() + } + for _, err := range c.errors { + t.Errorf("%v", err) + } +} + +// EventuallyWithT asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithT(t, func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + collect := new(CollectT) + ch := make(chan bool, 1) + + timer := time.NewTimer(waitFor) + defer timer.Stop() + + ticker := time.NewTicker(tick) + defer ticker.Stop() + + for tick := ticker.C; ; { + select { + case <-timer.C: + collect.Copy(t) + return Fail(t, "Condition never satisfied", msgAndArgs...) + case <-tick: + tick = nil + collect.Reset() + go func() { + condition(collect) + ch <- len(collect.errors) == 0 + }() + case v := <-ch: + if v { + return true + } + tick = ticker.C + } + } +} + // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) +// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/doc.go b/vendor/github.com/stretchr/testify/assert/doc.go index c9dccc4d6..4953981d3 100644 --- a/vendor/github.com/stretchr/testify/assert/doc.go +++ b/vendor/github.com/stretchr/testify/assert/doc.go @@ -1,39 +1,40 @@ // Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. // -// Example Usage +// # Example Usage // // The following is a complete example using assert in a standard test function: -// import ( -// "testing" -// "github.com/stretchr/testify/assert" -// ) // -// func TestSomething(t *testing.T) { +// import ( +// "testing" +// "github.com/stretchr/testify/assert" +// ) // -// var a string = "Hello" -// var b string = "Hello" +// func TestSomething(t *testing.T) { // -// assert.Equal(t, a, b, "The two words should be the same.") +// var a string = "Hello" +// var b string = "Hello" // -// } +// assert.Equal(t, a, b, "The two words should be the same.") +// +// } // // if you assert many times, use the format below: // -// import ( -// "testing" -// "github.com/stretchr/testify/assert" -// ) +// import ( +// "testing" +// "github.com/stretchr/testify/assert" +// ) // -// func TestSomething(t *testing.T) { -// assert := assert.New(t) +// func TestSomething(t *testing.T) { +// assert := assert.New(t) // -// var a string = "Hello" -// var b string = "Hello" +// var a string = "Hello" +// var b string = "Hello" // -// assert.Equal(a, b, "The two words should be the same.") -// } +// assert.Equal(a, b, "The two words should be the same.") +// } // -// Assertions +// # Assertions // // Assertions allow you to easily write test code, and are global funcs in the `assert` package. // All assertion functions take, as the first argument, the `*testing.T` object provided by the diff --git a/vendor/github.com/stretchr/testify/assert/http_assertions.go b/vendor/github.com/stretchr/testify/assert/http_assertions.go index 4ed341dd2..d8038c28a 100644 --- a/vendor/github.com/stretchr/testify/assert/http_assertions.go +++ b/vendor/github.com/stretchr/testify/assert/http_assertions.go @@ -23,7 +23,7 @@ func httpCode(handler http.HandlerFunc, method, url string, values url.Values) ( // HTTPSuccess asserts that a specified handler returns a success status code. // -// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) +// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -45,7 +45,7 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value // HTTPRedirect asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -67,7 +67,7 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu // HTTPError asserts that a specified handler returns an error status code. // -// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -89,7 +89,7 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values // HTTPStatusCode asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) +// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { @@ -124,7 +124,7 @@ func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) s // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { @@ -144,7 +144,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { diff --git a/vendor/github.com/stretchr/testify/mock/doc.go b/vendor/github.com/stretchr/testify/mock/doc.go index 7324128ef..d6b3c844c 100644 --- a/vendor/github.com/stretchr/testify/mock/doc.go +++ b/vendor/github.com/stretchr/testify/mock/doc.go @@ -1,17 +1,17 @@ // Package mock provides a system by which it is possible to mock your objects // and verify calls are happening as expected. // -// Example Usage +// # Example Usage // // The mock package provides an object, Mock, that tracks activity on another object. It is usually // embedded into a test object as shown below: // -// type MyTestObject struct { -// // add a Mock object instance -// mock.Mock +// type MyTestObject struct { +// // add a Mock object instance +// mock.Mock // -// // other fields go here as normal -// } +// // other fields go here as normal +// } // // When implementing the methods of an interface, you wire your functions up // to call the Mock.Called(args...) method, and return the appropriate values. @@ -19,25 +19,25 @@ // For example, to mock a method that saves the name and age of a person and returns // the year of their birth or an error, you might write this: // -// func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) { -// args := o.Called(firstname, lastname, age) -// return args.Int(0), args.Error(1) -// } +// func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) { +// args := o.Called(firstname, lastname, age) +// return args.Int(0), args.Error(1) +// } // // The Int, Error and Bool methods are examples of strongly typed getters that take the argument // index position. Given this argument list: // -// (12, true, "Something") +// (12, true, "Something") // // You could read them out strongly typed like this: // -// args.Int(0) -// args.Bool(1) -// args.String(2) +// args.Int(0) +// args.Bool(1) +// args.String(2) // // For objects of your own type, use the generic Arguments.Get(index) method and make a type assertion: // -// return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine) +// return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine) // // This may cause a panic if the object you are getting is nil (the type assertion will fail), in those // cases you should check for nil first. diff --git a/vendor/github.com/stretchr/testify/mock/mock.go b/vendor/github.com/stretchr/testify/mock/mock.go index e6ff8dfeb..f4b42e44f 100644 --- a/vendor/github.com/stretchr/testify/mock/mock.go +++ b/vendor/github.com/stretchr/testify/mock/mock.go @@ -3,6 +3,7 @@ package mock import ( "errors" "fmt" + "path" "reflect" "regexp" "runtime" @@ -13,6 +14,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/pmezard/go-difflib/difflib" "github.com/stretchr/objx" + "github.com/stretchr/testify/assert" ) @@ -99,7 +101,7 @@ func (c *Call) unlock() { // Return specifies the return arguments for the expectation. // -// Mock.On("DoSomething").Return(errors.New("failed")) +// Mock.On("DoSomething").Return(errors.New("failed")) func (c *Call) Return(returnArguments ...interface{}) *Call { c.lock() defer c.unlock() @@ -111,7 +113,7 @@ func (c *Call) Return(returnArguments ...interface{}) *Call { // Panic specifies if the functon call should fail and the panic message // -// Mock.On("DoSomething").Panic("test panic") +// Mock.On("DoSomething").Panic("test panic") func (c *Call) Panic(msg string) *Call { c.lock() defer c.unlock() @@ -123,14 +125,14 @@ func (c *Call) Panic(msg string) *Call { // Once indicates that that the mock should only return the value once. // -// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once() +// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once() func (c *Call) Once() *Call { return c.Times(1) } // Twice indicates that that the mock should only return the value twice. // -// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice() +// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice() func (c *Call) Twice() *Call { return c.Times(2) } @@ -138,7 +140,7 @@ func (c *Call) Twice() *Call { // Times indicates that that the mock should only return the indicated number // of times. // -// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5) +// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5) func (c *Call) Times(i int) *Call { c.lock() defer c.unlock() @@ -149,7 +151,7 @@ func (c *Call) Times(i int) *Call { // WaitUntil sets the channel that will block the mock's return until its closed // or a message is received. // -// Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second)) +// Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second)) func (c *Call) WaitUntil(w <-chan time.Time) *Call { c.lock() defer c.unlock() @@ -159,7 +161,7 @@ func (c *Call) WaitUntil(w <-chan time.Time) *Call { // After sets how long to block until the call returns // -// Mock.On("MyMethod", arg1, arg2).After(time.Second) +// Mock.On("MyMethod", arg1, arg2).After(time.Second) func (c *Call) After(d time.Duration) *Call { c.lock() defer c.unlock() @@ -171,10 +173,10 @@ func (c *Call) After(d time.Duration) *Call { // mocking a method (such as an unmarshaler) that takes a pointer to a struct and // sets properties in such struct // -// Mock.On("Unmarshal", AnythingOfType("*map[string]interface{}")).Return().Run(func(args Arguments) { -// arg := args.Get(0).(*map[string]interface{}) -// arg["foo"] = "bar" -// }) +// Mock.On("Unmarshal", AnythingOfType("*map[string]interface{}")).Return().Run(func(args Arguments) { +// arg := args.Get(0).(*map[string]interface{}) +// arg["foo"] = "bar" +// }) func (c *Call) Run(fn func(args Arguments)) *Call { c.lock() defer c.unlock() @@ -194,16 +196,18 @@ func (c *Call) Maybe() *Call { // On chains a new expectation description onto the mocked interface. This // allows syntax like. // -// Mock. -// On("MyMethod", 1).Return(nil). -// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error")) +// Mock. +// On("MyMethod", 1).Return(nil). +// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error")) +// //go:noinline func (c *Call) On(methodName string, arguments ...interface{}) *Call { return c.Parent.On(methodName, arguments...) } // Unset removes a mock handler from being called. -// test.On("func", mock.Anything).Unset() +// +// test.On("func", mock.Anything).Unset() func (c *Call) Unset() *Call { var unlockOnce sync.Once @@ -249,9 +253,9 @@ func (c *Call) Unset() *Call { // calls have been called as expected. The referenced calls may be from the // same mock instance and/or other mock instances. // -// Mock.On("Do").Return(nil).Notbefore( -// Mock.On("Init").Return(nil) -// ) +// Mock.On("Do").Return(nil).Notbefore( +// Mock.On("Init").Return(nil) +// ) func (c *Call) NotBefore(calls ...*Call) *Call { c.lock() defer c.unlock() @@ -334,7 +338,7 @@ func (m *Mock) fail(format string, args ...interface{}) { // On starts a description of an expectation of the specified method // being called. // -// Mock.On("MyMethod", arg1, arg2) +// Mock.On("MyMethod", arg1, arg2) func (m *Mock) On(methodName string, arguments ...interface{}) *Call { for _, arg := range arguments { if v := reflect.ValueOf(arg); v.Kind() == reflect.Func { @@ -424,6 +428,10 @@ func callString(method string, arguments Arguments, includeArgumentValues bool) if includeArgumentValues { var argVals []string for argIndex, arg := range arguments { + if _, ok := arg.(*FunctionalOptionsArgument); ok { + argVals = append(argVals, fmt.Sprintf("%d: %s", argIndex, arg)) + continue + } argVals = append(argVals, fmt.Sprintf("%d: %#v", argIndex, arg)) } argValsString = fmt.Sprintf("\n\t\t%s", strings.Join(argVals, "\n\t\t")) @@ -758,6 +766,7 @@ type AnythingOfTypeArgument string // name of the type to check for. Used in Diff and Assert. // // For example: +// // Assert(t, AnythingOfType("string"), AnythingOfType("int")) func AnythingOfType(t string) AnythingOfTypeArgument { return AnythingOfTypeArgument(t) @@ -780,6 +789,34 @@ func IsType(t interface{}) *IsTypeArgument { return &IsTypeArgument{t: t} } +// FunctionalOptionsArgument is a struct that contains the type and value of an functional option argument +// for use when type checking. +type FunctionalOptionsArgument struct { + value interface{} +} + +// String returns the string representation of FunctionalOptionsArgument +func (f *FunctionalOptionsArgument) String() string { + var name string + tValue := reflect.ValueOf(f.value) + if tValue.Len() > 0 { + name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String() + } + + return strings.Replace(fmt.Sprintf("%#v", f.value), "[]interface {}", name, 1) +} + +// FunctionalOptions returns an FunctionalOptionsArgument object containing the functional option type +// and the values to check of +// +// For example: +// Assert(t, FunctionalOptions("[]foo.FunctionalOption", foo.Opt1(), foo.Opt2())) +func FunctionalOptions(value ...interface{}) *FunctionalOptionsArgument { + return &FunctionalOptionsArgument{ + value: value, + } +} + // argumentMatcher performs custom argument matching, returning whether or // not the argument is matched by the expectation fixture function. type argumentMatcher struct { @@ -926,6 +963,29 @@ func (args Arguments) Diff(objects []interface{}) (string, int) { differences++ output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, reflect.TypeOf(t).Name(), reflect.TypeOf(actual).Name(), actualFmt) } + } else if reflect.TypeOf(expected) == reflect.TypeOf((*FunctionalOptionsArgument)(nil)) { + t := expected.(*FunctionalOptionsArgument).value + + var name string + tValue := reflect.ValueOf(t) + if tValue.Len() > 0 { + name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String() + } + + tName := reflect.TypeOf(t).Name() + if name != reflect.TypeOf(actual).String() && tValue.Len() != 0 { + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, tName, reflect.TypeOf(actual).Name(), actualFmt) + } else { + if ef, af := assertOpts(t, actual); ef == "" && af == "" { + // match + output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, tName, tName) + } else { + // not match + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, af, ef) + } + } } else { // normal checking @@ -1102,3 +1162,65 @@ var spewConfig = spew.ConfigState{ type tHelper interface { Helper() } + +func assertOpts(expected, actual interface{}) (expectedFmt, actualFmt string) { + expectedOpts := reflect.ValueOf(expected) + actualOpts := reflect.ValueOf(actual) + var expectedNames []string + for i := 0; i < expectedOpts.Len(); i++ { + expectedNames = append(expectedNames, funcName(expectedOpts.Index(i).Interface())) + } + var actualNames []string + for i := 0; i < actualOpts.Len(); i++ { + actualNames = append(actualNames, funcName(actualOpts.Index(i).Interface())) + } + if !assert.ObjectsAreEqual(expectedNames, actualNames) { + expectedFmt = fmt.Sprintf("%v", expectedNames) + actualFmt = fmt.Sprintf("%v", actualNames) + return + } + + for i := 0; i < expectedOpts.Len(); i++ { + expectedOpt := expectedOpts.Index(i).Interface() + actualOpt := actualOpts.Index(i).Interface() + + expectedFunc := expectedNames[i] + actualFunc := actualNames[i] + if expectedFunc != actualFunc { + expectedFmt = expectedFunc + actualFmt = actualFunc + return + } + + ot := reflect.TypeOf(expectedOpt) + var expectedValues []reflect.Value + var actualValues []reflect.Value + if ot.NumIn() == 0 { + return + } + + for i := 0; i < ot.NumIn(); i++ { + vt := ot.In(i).Elem() + expectedValues = append(expectedValues, reflect.New(vt)) + actualValues = append(actualValues, reflect.New(vt)) + } + + reflect.ValueOf(expectedOpt).Call(expectedValues) + reflect.ValueOf(actualOpt).Call(actualValues) + + for i := 0; i < ot.NumIn(); i++ { + if !assert.ObjectsAreEqual(expectedValues[i].Interface(), actualValues[i].Interface()) { + expectedFmt = fmt.Sprintf("%s %+v", expectedNames[i], expectedValues[i].Interface()) + actualFmt = fmt.Sprintf("%s %+v", expectedNames[i], actualValues[i].Interface()) + return + } + } + } + + return "", "" +} + +func funcName(opt interface{}) string { + n := runtime.FuncForPC(reflect.ValueOf(opt).Pointer()).Name() + return strings.TrimSuffix(path.Base(n), path.Ext(n)) +} diff --git a/vendor/golang.org/x/crypto/hkdf/hkdf.go b/vendor/golang.org/x/crypto/hkdf/hkdf.go new file mode 100644 index 000000000..f4ded5fee --- /dev/null +++ b/vendor/golang.org/x/crypto/hkdf/hkdf.go @@ -0,0 +1,95 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package hkdf implements the HMAC-based Extract-and-Expand Key Derivation +// Function (HKDF) as defined in RFC 5869. +// +// HKDF is a cryptographic key derivation function (KDF) with the goal of +// expanding limited input keying material into one or more cryptographically +// strong secret keys. +package hkdf // import "golang.org/x/crypto/hkdf" + +import ( + "crypto/hmac" + "errors" + "hash" + "io" +) + +// Extract generates a pseudorandom key for use with Expand from an input secret +// and an optional independent salt. +// +// Only use this function if you need to reuse the extracted key with multiple +// Expand invocations and different context values. Most common scenarios, +// including the generation of multiple keys, should use New instead. +func Extract(hash func() hash.Hash, secret, salt []byte) []byte { + if salt == nil { + salt = make([]byte, hash().Size()) + } + extractor := hmac.New(hash, salt) + extractor.Write(secret) + return extractor.Sum(nil) +} + +type hkdf struct { + expander hash.Hash + size int + + info []byte + counter byte + + prev []byte + buf []byte +} + +func (f *hkdf) Read(p []byte) (int, error) { + // Check whether enough data can be generated + need := len(p) + remains := len(f.buf) + int(255-f.counter+1)*f.size + if remains < need { + return 0, errors.New("hkdf: entropy limit reached") + } + // Read any leftover from the buffer + n := copy(p, f.buf) + p = p[n:] + + // Fill the rest of the buffer + for len(p) > 0 { + if f.counter > 1 { + f.expander.Reset() + } + f.expander.Write(f.prev) + f.expander.Write(f.info) + f.expander.Write([]byte{f.counter}) + f.prev = f.expander.Sum(f.prev[:0]) + f.counter++ + + // Copy the new batch into p + f.buf = f.prev + n = copy(p, f.buf) + p = p[n:] + } + // Save leftovers for next run + f.buf = f.buf[n:] + + return need, nil +} + +// Expand returns a Reader, from which keys can be read, using the given +// pseudorandom key and optional context info, skipping the extraction step. +// +// The pseudorandomKey should have been generated by Extract, or be a uniformly +// random or pseudorandom cryptographically strong key. See RFC 5869, Section +// 3.3. Most common scenarios will want to use New instead. +func Expand(hash func() hash.Hash, pseudorandomKey, info []byte) io.Reader { + expander := hmac.New(hash, pseudorandomKey) + return &hkdf{expander, expander.Size(), info, 1, nil, nil} +} + +// New returns a Reader, from which keys can be read, using the given hash, +// secret, salt and context info. Salt and info can be nil. +func New(hash func() hash.Hash, secret, salt, info []byte) io.Reader { + prk := Extract(hash, secret, salt) + return Expand(hash, prk, info) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index eee94fdd8..a47025201 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,9 +1,23 @@ -# github.com/IBM/idemix v0.0.2-0.20230510082947-a0c3ee5ebe35 +# github.com/IBM/idemix v0.0.2-0.20230914074304-f8eb4a0f6606 ## explicit; go 1.19 -github.com/IBM/idemix/bccsp/schemes +github.com/IBM/idemix/bccsp +github.com/IBM/idemix/bccsp/handlers +github.com/IBM/idemix/bccsp/keystore +github.com/IBM/idemix/bccsp/schemes/dlog/bridge github.com/IBM/idemix/bccsp/schemes/dlog/crypto github.com/IBM/idemix/bccsp/schemes/dlog/crypto/translator/amcl -# github.com/IBM/mathlib v0.0.3-0.20231011094432-44ee0eb539da +github.com/IBM/idemix/common/flogging +github.com/IBM/idemix/common/flogging/fabenc +# github.com/IBM/idemix/bccsp/schemes/aries v0.0.0-20230818093228-308e96f529c9 +## explicit; go 1.19 +github.com/IBM/idemix/bccsp/schemes/aries +# github.com/IBM/idemix/bccsp/schemes/weak-bb v0.0.0-20230818093228-308e96f529c9 +## explicit; go 1.19 +github.com/IBM/idemix/bccsp/schemes/weak-bb +# github.com/IBM/idemix/bccsp/types v0.0.0-20230914074304-f8eb4a0f6606 +## explicit; go 1.19 +github.com/IBM/idemix/bccsp/types +# github.com/IBM/mathlib v0.0.3-0.20230831091907-c532c4d3b65c ## explicit; go 1.18 github.com/IBM/mathlib github.com/IBM/mathlib/driver @@ -14,6 +28,9 @@ github.com/IBM/mathlib/driver/kilic # github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible ## explicit github.com/Knetic/govaluate +# github.com/ale-linux/aries-framework-go/component/kmscrypto v0.0.0-20230817163708-4b3de6d91874 +## explicit; go 1.19 +github.com/ale-linux/aries-framework-go/component/kmscrypto/crypto/primitive/bbs12381g2pub # github.com/beorn7/perks v1.0.1 ## explicit; go 1.11 github.com/beorn7/perks/quantile @@ -319,8 +336,8 @@ github.com/spf13/viper # github.com/stretchr/objx v0.5.0 ## explicit; go 1.12 github.com/stretchr/objx -# github.com/stretchr/testify v1.8.2 -## explicit; go 1.13 +# github.com/stretchr/testify v1.8.4 +## explicit; go 1.20 github.com/stretchr/testify/assert github.com/stretchr/testify/mock # github.com/subosito/gotenv v1.2.0 @@ -367,6 +384,7 @@ golang.org/x/crypto/blowfish golang.org/x/crypto/cryptobyte golang.org/x/crypto/cryptobyte/asn1 golang.org/x/crypto/ed25519 +golang.org/x/crypto/hkdf golang.org/x/crypto/ocsp golang.org/x/crypto/pkcs12 golang.org/x/crypto/pkcs12/internal/rc2