github.com/zcqzcg/fabric-ca@v2.0.0-alpha.0.20200416163940-d878ee6db75a+incompatible/lib/server/idemix/issuer_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package idemix_test 8 9 import ( 10 "crypto/ecdsa" 11 "crypto/elliptic" 12 "crypto/rand" 13 "fmt" 14 "io/ioutil" 15 "os" 16 "path/filepath" 17 "testing" 18 19 "github.com/hyperledger/fabric-ca/lib" 20 dbutil "github.com/hyperledger/fabric-ca/lib/server/db/util" 21 . "github.com/hyperledger/fabric-ca/lib/server/idemix" 22 "github.com/hyperledger/fabric-ca/lib/server/idemix/mocks" 23 dmocks "github.com/hyperledger/fabric-ca/lib/server/idemix/mocks" 24 "github.com/hyperledger/fabric-ca/util" 25 "github.com/hyperledger/fabric/bccsp" 26 "github.com/hyperledger/fabric/idemix" 27 "github.com/kisielk/sqlstruct" 28 "github.com/pkg/errors" 29 "github.com/stretchr/testify/assert" 30 ) 31 32 func TestNewIssuer(t *testing.T) { 33 lib := new(mocks.Lib) 34 cfg := &Config{ 35 NonceExpiration: "15", 36 NonceSweepInterval: "15", 37 } 38 issuer := NewIssuer("ca1", ".", cfg, util.GetDefaultBCCSP(), lib) 39 assert.NotNil(t, issuer) 40 } 41 42 func TestInit(t *testing.T) { 43 testdir, err := ioutil.TempDir(".", "issuerinittest") 44 if err != nil { 45 t.Fatalf("Failed to create temp directory: %s", err.Error()) 46 } 47 defer os.RemoveAll(testdir) 48 err = os.MkdirAll(filepath.Join(testdir, "msp/keystore"), 0777) 49 if err != nil { 50 t.Fatalf("Failed to create directory: %s", err.Error()) 51 } 52 53 db, issuer := getIssuer(t, testdir, false, false) 54 assert.NotNil(t, issuer) 55 err = issuer.Init(false, nil, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) 56 assert.NoError(t, err, "Init should not return an error if db is nil") 57 58 err = issuer.Init(false, db, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) 59 assert.NoError(t, err) 60 61 ik, err := issuer.IssuerPublicKey() 62 assert.NoError(t, err, "IssuerPublicKey should not return an error") 63 assert.NotNil(t, ik) 64 ctx := new(mocks.ServerRequestCtx) 65 ctx.On("IsBasicAuth").Return(true) 66 ctx.On("BasicAuthentication").Return("", errors.New("Authentication error")) 67 ctx.On("TokenAuthentication").Return("", errors.New("Authentication error")) 68 _, err = issuer.IssueCredential(ctx) 69 assert.Error(t, err, "IssuerCredential should fail") 70 _, err = issuer.GetCRI(ctx) 71 assert.Error(t, err, "GetCRI should fail") 72 } 73 74 func TestInitDBNotInitialized(t *testing.T) { 75 cfg := &Config{ 76 NonceExpiration: "15s", 77 NonceSweepInterval: "15m", 78 } 79 var db *dmocks.FabricCADB 80 issuer := NewIssuer("ca1", ".", cfg, util.GetDefaultBCCSP(), NewLib()) 81 err := issuer.Init(false, db, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) 82 assert.NoError(t, err) 83 84 db = new(dmocks.FabricCADB) 85 db.On("IsInitialized").Return(false) 86 issuer = NewIssuer("ca1", ".", cfg, util.GetDefaultBCCSP(), NewLib()) 87 err = issuer.Init(false, db, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) 88 assert.NoError(t, err) 89 } 90 91 func TestInitExistingIssuerCredential(t *testing.T) { 92 testdir, err := ioutil.TempDir(".", "issuerinittest") 93 if err != nil { 94 t.Fatalf("Failed to create temp directory: %s", err.Error()) 95 } 96 defer os.RemoveAll(testdir) 97 err = os.MkdirAll(filepath.Join(testdir, "msp/keystore"), 0777) 98 if err != nil { 99 t.Fatalf("Failed to create directory: %s", err.Error()) 100 } 101 err = lib.CopyFile(testPublicKeyFile, filepath.Join(testdir, "IssuerPublicKey")) 102 if err != nil { 103 t.Fatalf("Failed to copy file: %s", err.Error()) 104 } 105 err = lib.CopyFile(testSecretKeyFile, filepath.Join(testdir, "msp/keystore/IssuerSecretKey")) 106 if err != nil { 107 t.Fatalf("Failed to copy file: %s", err.Error()) 108 } 109 110 db, issuer := getIssuer(t, testdir, false, false) 111 assert.NotNil(t, issuer) 112 113 secrekeyfile := filepath.Join(testdir, "msp/keystore/IssuerSecretKey") 114 secrekeyFileInfo, err := os.Stat(secrekeyfile) 115 if err != nil { 116 t.Fatalf("os.Stat failed on test dir: %s", err) 117 } 118 oldmode := secrekeyFileInfo.Mode() 119 err = os.Chmod(secrekeyfile, 0000) 120 if err != nil { 121 t.Fatalf("Chmod on %s failed: %s", secrekeyFileInfo.Name(), err) 122 } 123 err = issuer.Init(false, db, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) 124 assert.Error(t, err, "Init should fail if it fails to load issuer credential") 125 126 err = os.Chmod(secrekeyfile, oldmode) 127 if err != nil { 128 t.Fatalf("Chmod on %s failed: %s", testdir, err) 129 } 130 err = issuer.Init(false, db, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) 131 assert.NoError(t, err) 132 } 133 func TestInitRenewTrue(t *testing.T) { 134 testdir, err := ioutil.TempDir(".", "issuerinittest") 135 if err != nil { 136 t.Fatalf("Failed to create temp directory: %s", err.Error()) 137 } 138 defer os.RemoveAll(testdir) 139 db, issuer := getIssuer(t, testdir, true, false) 140 assert.NotNil(t, issuer) 141 142 err = issuer.Init(true, db, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) 143 assert.Error(t, err, "Init should fail if it fails to generate random number") 144 145 db, issuer = getIssuer(t, testdir, false, true) 146 assert.NotNil(t, issuer) 147 err = issuer.Init(true, db, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) 148 assert.Error(t, err, "Init should fail if it fails to create new issuer key") 149 150 db, issuer = getIssuer(t, testdir, false, false) 151 assert.NotNil(t, issuer) 152 153 testdataInfo, err := os.Stat(testdir) 154 if err != nil { 155 t.Fatalf("os.Stat failed on test dir: %s", err) 156 } 157 oldmode := testdataInfo.Mode() 158 err = os.Chmod(testdir, 0000) 159 if err != nil { 160 t.Fatalf("Chmod on %s failed: %s", testdataInfo.Name(), err) 161 } 162 defer func() { 163 err = os.Chmod(testdir, oldmode) 164 if err != nil { 165 t.Fatalf("Chmod on %s failed: %s", testdir, err) 166 } 167 }() 168 err = issuer.Init(true, db, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) 169 assert.Error(t, err, "Init should fail if it fails to store issuer credential") 170 } 171 172 func TestVerifyTokenError(t *testing.T) { 173 testdir, err := ioutil.TempDir(".", "verifytokentesterror") 174 if err != nil { 175 t.Fatalf("Failed to create temp directory: %s", err.Error()) 176 } 177 defer os.RemoveAll(testdir) 178 179 err = os.MkdirAll(filepath.Join(testdir, "msp/keystore"), 0777) 180 if err != nil { 181 t.Fatalf("Failed to create directory: %s", err.Error()) 182 } 183 err = lib.CopyFile(testPublicKeyFile, filepath.Join(testdir, "IssuerPublicKey")) 184 if err != nil { 185 t.Fatalf("Failed to copy file: %s", err.Error()) 186 } 187 err = lib.CopyFile(testSecretKeyFile, filepath.Join(testdir, "msp/keystore/IssuerSecretKey")) 188 if err != nil { 189 t.Fatalf("Failed to copy file: %s", err.Error()) 190 } 191 192 db, issuer := getIssuer(t, testdir, false, false) 193 assert.NotNil(t, issuer) 194 195 _, err = issuer.VerifyToken("idemix.1.foo.blah", "", "", []byte{}) 196 assert.Error(t, err, "VerifyToken should fail as issuer is not initialized") 197 198 err = issuer.Init(false, db, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) 199 assert.NoError(t, err) 200 201 _, err = issuer.VerifyToken("idemix.1.foo", "", "", []byte{}) 202 assert.Error(t, err, "VerifyToken should fail if the auth header does not have four parts separated by '.'") 203 204 _, err = issuer.VerifyToken("idemix.2.foo.bar", "", "", []byte{}) 205 assert.Error(t, err, "VerifyToken should fail if the auth header does not have correct version") 206 207 db.On("Rebind", SelectCredentialByIDSQL).Return(SelectCredentialByIDSQL) 208 credRecords := []CredRecord{} 209 sqlstr := fmt.Sprintf(SelectCredentialByIDSQL, sqlstruct.Columns(CredRecord{})) 210 db.On("Select", "GetCredentialsByID", &credRecords, sqlstr, "foo").Return(errors.New("db error getting creds for user")) 211 212 _, err = issuer.VerifyToken("idemix.1.foo.sig", "", "", []byte{}) 213 assert.Error(t, err, "VerifyToken should fail if there is error looking up enrollment id in the database") 214 } 215 216 func TestVerifyTokenNoCreds(t *testing.T) { 217 testdir, err := ioutil.TempDir(".", "verifytokentestnocreds") 218 if err != nil { 219 t.Fatalf("Failed to create temp directory: %s", err.Error()) 220 } 221 defer os.RemoveAll(testdir) 222 223 err = os.MkdirAll(filepath.Join(testdir, "msp/keystore"), 0777) 224 if err != nil { 225 t.Fatalf("Failed to create directory: %s", err.Error()) 226 } 227 err = lib.CopyFile(testPublicKeyFile, filepath.Join(testdir, "IssuerPublicKey")) 228 if err != nil { 229 t.Fatalf("Failed to copy file: %s", err.Error()) 230 } 231 err = lib.CopyFile(testSecretKeyFile, filepath.Join(testdir, "msp/keystore/IssuerSecretKey")) 232 if err != nil { 233 t.Fatalf("Failed to copy file: %s", err.Error()) 234 } 235 236 db, issuer := getIssuer(t, testdir, false, false) 237 assert.NotNil(t, issuer) 238 239 err = issuer.Init(false, db, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) 240 assert.NoError(t, err) 241 242 db.On("Rebind", SelectCredentialByIDSQL).Return(SelectCredentialByIDSQL) 243 credRecords := []CredRecord{} 244 sqlstr := fmt.Sprintf(SelectCredentialByIDSQL, sqlstruct.Columns(CredRecord{})) 245 f := getCredsSelectFunc(t, &credRecords, false) 246 db.On("Select", "GetCredentialsByID", &credRecords, sqlstr, "foo").Return(f) 247 248 _, err = issuer.VerifyToken("idemix.1.foo.sig", "", "", []byte{}) 249 assert.Error(t, err, "VerifyToken should fail if the enrollment id does not have creds") 250 } 251 252 func TestVerifyTokenBadSignatureEncoding(t *testing.T) { 253 testdir, err := ioutil.TempDir(".", "verifytokentestbadsigencoding") 254 if err != nil { 255 t.Fatalf("Failed to create temp directory: %s", err.Error()) 256 } 257 defer os.RemoveAll(testdir) 258 259 err = os.MkdirAll(filepath.Join(testdir, "msp/keystore"), 0777) 260 if err != nil { 261 t.Fatalf("Failed to create directory: %s", err.Error()) 262 } 263 err = lib.CopyFile(testPublicKeyFile, filepath.Join(testdir, "IssuerPublicKey")) 264 if err != nil { 265 t.Fatalf("Failed to copy file: %s", err.Error()) 266 } 267 err = lib.CopyFile(testSecretKeyFile, filepath.Join(testdir, "msp/keystore/IssuerSecretKey")) 268 if err != nil { 269 t.Fatalf("Failed to copy file: %s", err.Error()) 270 } 271 272 db, issuer := getIssuer(t, testdir, false, false) 273 assert.NotNil(t, issuer) 274 275 err = issuer.Init(false, db, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) 276 assert.NoError(t, err) 277 278 db.On("Rebind", SelectCredentialByIDSQL).Return(SelectCredentialByIDSQL) 279 credRecords := []CredRecord{} 280 sqlstr := fmt.Sprintf(SelectCredentialByIDSQL, sqlstruct.Columns(CredRecord{})) 281 f := getCredsSelectFunc(t, &credRecords, true) 282 db.On("Select", "GetCredentialsByID", &credRecords, sqlstr, "foo").Return(f) 283 284 _, err = issuer.VerifyToken("idemix.1.foo.sig", "", "", []byte{}) 285 assert.Error(t, err, "VerifyToken should fail if the signature is not in base64 format") 286 assert.NotEqual(t, err.Error(), "errer") 287 } 288 289 func TestVerifyTokenBadSignature(t *testing.T) { 290 testdir, err := ioutil.TempDir(".", "verifytokentestbadsig") 291 if err != nil { 292 t.Fatalf("Failed to create temp directory: %s", err.Error()) 293 } 294 defer os.RemoveAll(testdir) 295 296 err = os.MkdirAll(filepath.Join(testdir, "msp/keystore"), 0777) 297 if err != nil { 298 t.Fatalf("Failed to create directory: %s", err.Error()) 299 } 300 err = lib.CopyFile(testPublicKeyFile, filepath.Join(testdir, "IssuerPublicKey")) 301 if err != nil { 302 t.Fatalf("Failed to copy file: %s", err.Error()) 303 } 304 err = lib.CopyFile(testSecretKeyFile, filepath.Join(testdir, "msp/keystore/IssuerSecretKey")) 305 if err != nil { 306 t.Fatalf("Failed to copy file: %s", err.Error()) 307 } 308 309 db, issuer := getIssuer(t, testdir, false, false) 310 assert.NotNil(t, issuer) 311 312 err = issuer.Init(false, db, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) 313 assert.NoError(t, err) 314 315 db.On("Rebind", SelectCredentialByIDSQL).Return(SelectCredentialByIDSQL) 316 credRecords := []CredRecord{} 317 sqlstr := fmt.Sprintf(SelectCredentialByIDSQL, sqlstruct.Columns(CredRecord{})) 318 f := getCredsSelectFunc(t, &credRecords, true) 319 db.On("Select", "GetCredentialsByID", &credRecords, sqlstr, "admin").Return(f) 320 321 sig := util.B64Encode([]byte("hello")) 322 _, err = issuer.VerifyToken("idemix.1.admin."+sig, "", "", []byte{}) 323 assert.Error(t, err, "VerifyToken should fail if the signature is not valid") 324 325 digest, err := util.GetDefaultBCCSP().Hash([]byte(sig), &bccsp.SHAOpts{}) 326 if err != nil { 327 t.Fatalf("Failed to get hash of the message: %s", err.Error()) 328 } 329 _, err = issuer.VerifyToken("idemix.1.admin.CkQKIAoanxNH9nO5ivQy94e+DH+SiwkkBhYeNbtyQhM1HD7FEiBbBcMVcCW9HoJe5KWMtyvO6a4UtB4xo2x/SV7xvxcVvBJECiBugYjF0AZ8lWvaeKCXtEbPvawQye7RK0m5SpQzEwcu/RIgioEuVacQR5DroKwgAZi3ALClpCLJFjlRwVv7w2zJcQQaRAogeAU3ZnfcA60kGIm6gHKGTRrI3O9sbkpdHt/UIF+Tz5sSIHGfTP5B7Ocb43q3sewpuqIjDyvFEzIeBpummJD4MPB5IiAewOhliKfwXta7pSCIMlfKqmuJbhAwhJl7vJdhfEW05iogGY6MfvsdO+HvQdSmlIexEBgl51KsFCO6MrAZbms/hLAyIHbqzC8f7sliJ6Hzn65JZKUyHXiAnOM3iydZ7gntoYXxOiClzG32BL3M4MyQGHz6SP8Aozxh3u0dATr0uxOOI6p94EIgO90ealPZ51ZXP+JsAWwLePpyX+lgegF0Gp002uFyv0tKIFRSBfhnRqm7Dk1VbG1hSsl7AJU8nzzYZJZKHRFrhdvGUiCWUu3nvjr5TEFtF5eOMp5XTPXmUNTq8k3SLckY1o35mlIgOeJtkxDc7NtKAiF+cz+cIsv1MIQ3qGXj0nwoMjnHvMJSIALGJWjFKVhK9B9P8BOkO03iMwzNJJdSeA8MIRGyk5WCWiCGix0AHQA29jHVOCaCrBZUVlqBRLa5Kzpftk0jp3LKXmJECiDheCgd36mEjsr1D4Sm+cbtE3XKAdRI2dLq5bFQZqN4/RIgNbxez4+fxVsRuGu8ooFkfem2C5/+1z3QDzyu8fu3fyVqID34eII73Km/SviYxAoHZ91HXIHXhGwid4DFO+xuGI7ycogBCiD+DDNQtMlsIChWD1d8KJE6zhxTmhK/hDzSJha2icCe+xIgTqZgV3OKwFTbWuHGN9gTuSTdeOKH0DWJ0mntNKN+aisaIHAgRufFQqOzdncNdRJOPlHvyyR1jWFYSOkJtIG+3Cf/IiAFVOO804jCkELupkkpfrKfi0y+gIIamLPgEoERSq0Em3pgkd4c0QZIUDeyRVBgwDj7aTk8J+xzdGZSCgIt8RpuKoxmfuDV2SlFfw/fVZqfPH02+jYeyqxbf7FD8vo5dstEpLHy86Yno6zr1bXLDLe34r2XIIH6KrYFI3gYAsQhzzd/gAEBigEA", "", "", digest) 330 assert.Error(t, err, "VerifyToken should fail signature is valid but verification fails") 331 } 332 333 func TestIsToken(t *testing.T) { 334 token := "idemixx.1.foo.blah" 335 assert.False(t, IsToken(token)) 336 token = "foo.sig" 337 assert.False(t, IsToken(token)) 338 token = "idemix.1.foo.sig" 339 assert.True(t, IsToken(token)) 340 } 341 342 func TestRevocationPublicKey(t *testing.T) { 343 testdir, err := ioutil.TempDir(".", "revocationpubkeytest") 344 if err != nil { 345 t.Fatalf("Failed to create temp directory: %s", err.Error()) 346 } 347 defer os.RemoveAll(testdir) 348 349 err = os.MkdirAll(filepath.Join(testdir, "msp/keystore"), 0777) 350 if err != nil { 351 t.Fatalf("Failed to create directory: %s", err.Error()) 352 } 353 err = lib.CopyFile(testPublicKeyFile, filepath.Join(testdir, "IssuerPublicKey")) 354 if err != nil { 355 t.Fatalf("Failed to copy file: %s", err.Error()) 356 } 357 err = lib.CopyFile(testSecretKeyFile, filepath.Join(testdir, "msp/keystore/IssuerSecretKey")) 358 if err != nil { 359 t.Fatalf("Failed to copy file: %s", err.Error()) 360 } 361 362 db, issuer := getIssuer(t, testdir, false, false) 363 assert.NotNil(t, issuer) 364 365 err = issuer.Init(false, db, &dbutil.Levels{Credential: 1, RAInfo: 1, Nonce: 1}) 366 assert.NoError(t, err, "Init should not return an error") 367 368 _, err = issuer.RevocationPublicKey() 369 assert.NoError(t, err, "RevocationPublicKey should not return an error") 370 } 371 372 func getIssuer(t *testing.T, testDir string, getranderror, newIssuerKeyerror bool) (*dmocks.FabricCADB, Issuer) { 373 err := os.MkdirAll(filepath.Join(testDir, "msp/keystore"), 0777) 374 if err != nil { 375 t.Fatalf("Failed to create directory: %s", err.Error()) 376 } 377 378 db := new(dmocks.FabricCADB) 379 380 tx := new(dmocks.FabricCATx) 381 tx.On("Commit").Return(nil) 382 tx.On("Rollback").Return(nil) 383 tx.On("Rebind", SelectRAInfo).Return(SelectRAInfo) 384 tx.On("Rebind", UpdateNextHandle).Return(UpdateNextHandle) 385 tx.On("Exec", UpdateNextHandle, 2, 1).Return(nil, nil) 386 rcInfos := []RevocationAuthorityInfo{} 387 f1 := getTxSelectFunc(t, &rcInfos, 1, false, true) 388 tx.On("Select", &rcInfos, SelectRAInfo).Return(f1) 389 390 db.On("BeginTx").Return(tx) 391 db.On("IsInitialized").Return(true) 392 393 lib := new(mocks.Lib) 394 rnd, err := idemix.GetRand() 395 if err != nil { 396 t.Fatalf("Failed to get random number: %s", err.Error()) 397 } 398 ik, err := idemix.NewIssuerKey(GetAttributeNames(), rnd) 399 if err != nil { 400 t.Fatalf("Failed to generate issuer key: %s", err.Error()) 401 } 402 if getranderror { 403 lib.On("GetRand").Return(nil, errors.New("Failed to generate random number")) 404 } else { 405 lib.On("GetRand").Return(rnd, nil) 406 } 407 408 if newIssuerKeyerror { 409 lib.On("NewIssuerKey", GetAttributeNames(), rnd).Return(nil, errors.New("Failed to generate new issuer key")) 410 } else { 411 lib.On("NewIssuerKey", GetAttributeNames(), rnd).Return(ik, nil) 412 } 413 414 key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) 415 if err != nil { 416 t.Fatalf("Failed to generate key: %s", err.Error()) 417 } 418 lib.On("GenerateLongTermRevocationKey").Return(key, nil) 419 420 cfg := &Config{ 421 RHPoolSize: 100, 422 NonceExpiration: "15s", 423 NonceSweepInterval: "15m", 424 } 425 issuer := NewIssuer("ca1", testDir, cfg, util.GetDefaultBCCSP(), lib) 426 427 f := getSelectFunc(t, true, false) 428 429 rcInfosForSelect := []RevocationAuthorityInfo{} 430 db.On("Select", "GetRAInfo", &rcInfosForSelect, SelectRAInfo).Return(f) 431 rcinfo := RevocationAuthorityInfo{ 432 Epoch: 1, 433 NextRevocationHandle: 1, 434 LastHandleInPool: 100, 435 Level: 1, 436 } 437 result := new(dmocks.Result) 438 result.On("RowsAffected").Return(int64(1), nil) 439 db.On("NamedExec", "AddRAInfo", InsertRAInfo, &rcinfo).Return(result, nil) 440 441 return db, issuer 442 } 443 444 func getCredsSelectFunc(t *testing.T, creds *[]CredRecord, isAppend bool) func(string, interface{}, string, ...interface{}) error { 445 return func(funcName string, dest interface{}, query string, args ...interface{}) error { 446 credRecs := dest.(*[]CredRecord) 447 cred := CredRecord{ 448 ID: "foo", 449 Status: "active", 450 Cred: "", 451 } 452 453 if isAppend { 454 //*creds = append(*creds, cred) 455 *credRecs = append(*credRecs, cred) 456 } 457 return nil 458 } 459 }