gitee.com/zhaochuninhefei/fabric-ca-gm@v0.0.2/lib/server/idemix/nonce.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package idemix 8 9 import ( 10 "fmt" 11 "time" 12 13 "gitee.com/zhaochuninhefei/fabric-ca-gm/internal/pkg/util" 14 "gitee.com/zhaochuninhefei/fabric-ca-gm/lib/server/db" 15 "gitee.com/zhaochuninhefei/fabric-gm/idemix" 16 log "gitee.com/zhaochuninhefei/zcgolog/zclog" 17 fp256bn "github.com/hyperledger/fabric-amcl/amcl/FP256BN" 18 "github.com/pkg/errors" 19 ) 20 21 const ( 22 // InsertNonce is the SQL for inserting a nonce 23 InsertNonce = "INSERT into nonces(val, expiry, level) VALUES (:val, :expiry, :level)" 24 // SelectNonce is query string for getting a particular nonce 25 SelectNonce = "SELECT * FROM nonces WHERE (val = ?)" 26 // RemoveNonce is the query string for removing a specified nonce 27 RemoveNonce = "DELETE FROM nonces WHERE (val = ?)" 28 // RemoveExpiredNonces is the SQL string removing expired nonces 29 RemoveExpiredNonces = "DELETE FROM nonces WHERE (expiry < ?)" 30 // DefaultNonceExpiration is the default value for nonce expiration 31 DefaultNonceExpiration = "15s" 32 // DefaultNonceSweepInterval is the default value for nonce sweep interval 33 DefaultNonceSweepInterval = "15m" 34 ) 35 36 // Nonce represents a nonce 37 type Nonce struct { 38 Val string `db:"val"` 39 Expiry time.Time `db:"expiry"` 40 Level int `db:"level"` 41 } 42 43 // NonceManager represents nonce manager that is responsible for 44 // getting a new nonce 45 type NonceManager interface { 46 // GetNonce creates a nonce, stores it in the database and returns it 47 GetNonce() (*fp256bn.BIG, error) 48 // CheckNonce checks if the specified nonce exists in the database and has not expired 49 CheckNonce(nonce *fp256bn.BIG) error 50 // SweepExpiredNonces removes expired nonces from the database 51 SweepExpiredNonces() error 52 } 53 54 // Clock provides time related functions 55 type Clock interface { 56 Now() time.Time 57 } 58 59 // nonceManager implements NonceManager interface 60 type nonceManager struct { 61 nonceExpiration time.Duration 62 nonceSweepInterval time.Duration 63 clock Clock 64 issuer MyIssuer 65 level int 66 } 67 68 // NewNonceManager returns an instance of an object that implements NonceManager interface 69 func NewNonceManager(issuer MyIssuer, clock Clock, level int) (NonceManager, error) { 70 var err error 71 mgr := &nonceManager{ 72 issuer: issuer, 73 clock: clock, 74 level: level, 75 } 76 opts := issuer.Config() 77 mgr.nonceExpiration, err = time.ParseDuration(opts.NonceExpiration) 78 if err != nil { 79 return nil, errors.Wrapf(err, fmt.Sprintf("Failed to parse idemix.nonceexpiration config option while initializing Nonce manager for Issuer '%s'", 80 issuer.Name())) 81 } 82 mgr.nonceSweepInterval, err = time.ParseDuration(opts.NonceSweepInterval) 83 if err != nil { 84 return nil, errors.Wrapf(err, fmt.Sprintf("Failed to parse idemix.noncesweepinterval config option while initializing Nonce manager for Issuer '%s'", 85 issuer.Name())) 86 } 87 return mgr, nil 88 } 89 90 // GetNonce returns a new nonce 91 func (nm *nonceManager) GetNonce() (*fp256bn.BIG, error) { 92 idmixLib := nm.issuer.IdemixLib() 93 nonce, err := idmixLib.RandModOrder(nm.issuer.IdemixRand()) 94 if err != nil { 95 return nil, err 96 } 97 nonceBytes := idemix.BigToBytes(nonce) 98 err = nm.insertNonceInDB(&Nonce{ 99 Val: util.B64Encode(nonceBytes), 100 Expiry: nm.clock.Now().UTC().Add(nm.nonceExpiration), 101 Level: nm.level, 102 }) 103 if err != nil { 104 log.Errorf("Failed to store nonce: %s", err.Error()) 105 return nil, errors.WithMessage(err, "Failed to store nonce") 106 } 107 return nonce, nil 108 } 109 110 // CheckNonce checks if the specified nonce is valid (is in DB and has not expired) 111 // and the nonce is removed from DB 112 func (nm *nonceManager) CheckNonce(nonce *fp256bn.BIG) error { 113 nonceBytes := idemix.BigToBytes(nonce) 114 queryParam := util.B64Encode(nonceBytes) 115 nonceRec, err := doTransaction("CheckNonce", nm.issuer.DB(), nm.getNonceFromDB, queryParam) 116 if err != nil { 117 return err 118 } 119 nonceFromDB := nonceRec.(Nonce) 120 log.Debugf("Retrieved nonce from DB: %+v, %s", nonceRec, queryParam) 121 122 if nonceFromDB.Val != queryParam || nonceFromDB.Expiry.Before(time.Now().UTC()) { 123 return errors.New("Nonce is either unknown or has expired") 124 } 125 return nil 126 } 127 128 // SweepExpiredNonces sweeps expired nonces 129 func (nm *nonceManager) SweepExpiredNonces() error { 130 return nm.sweep(nm.clock.Now().UTC()) 131 } 132 133 // StartNonceSweeper starts a separate thread that will remove expired 134 // nonces at the interval specified by the idemix.noncesweepinterval. This 135 // function should be called while initializing the server. 136 func (nm *nonceManager) StartNonceSweeper() { 137 go func() { 138 ticker := time.NewTicker(nm.nonceSweepInterval) 139 for t := range ticker.C { 140 nm.sweep(t.UTC()) 141 } 142 }() 143 } 144 145 // sweep deletes all nonces that have expired (whose expiry is less than current timestamp) 146 func (nm *nonceManager) sweep(curTime time.Time) error { 147 log.Debugf("Cleaning up expired nonces for CA '%s'", nm.issuer.Name()) 148 return nm.removeExpiredNoncesFromDB(curTime) 149 } 150 151 // Gets the specified nonce from DB and removes it from the DB 152 func (nm *nonceManager) getNonceFromDB(tx db.FabricCATx, args ...interface{}) (interface{}, error) { 153 nonces := []Nonce{} 154 err := tx.Select("GetNonce", &nonces, tx.Rebind(SelectNonce), args...) 155 if err != nil { 156 log.Errorf("Failed to get nonce from DB: %s", err.Error()) 157 return nil, errors.New("Failed to retrieve nonce from the datastore") 158 } 159 if len(nonces) == 0 { 160 return nil, errors.New("Nonce not found in the datastore") 161 } 162 result, err := tx.Exec("GetNonce", tx.Rebind(RemoveNonce), args...) 163 if err != nil { 164 log.Errorf("Failed to remove nonce %s from DB: %s", args[0], err.Error()) 165 return nonces[0], nil 166 } 167 numRowsAffected, err := result.RowsAffected() 168 if numRowsAffected != 1 { 169 log.Errorf("Tried to remove one nonce from DB but %d were removed", numRowsAffected) 170 } 171 return nonces[0], nil 172 } 173 174 func (nm *nonceManager) removeExpiredNoncesFromDB(curTime time.Time) error { 175 _, err := nm.issuer.DB().Exec("RemoveExpiredNonces", nm.issuer.DB().Rebind(RemoveExpiredNonces), curTime) 176 if err != nil { 177 log.Errorf("Failed to remove expired nonces from DB for CA '%s': %s", nm.issuer.Name(), err.Error()) 178 return errors.New("Failed to remove expired nonces from DB") 179 } 180 return nil 181 } 182 183 func (nm *nonceManager) insertNonceInDB(nonce *Nonce) error { 184 res, err := nm.issuer.DB().NamedExec("InsertNonce", InsertNonce, nonce) 185 if err != nil { 186 log.Errorf("Failed to add nonce to DB: %s", err.Error()) 187 return errors.New("Failed to add nonce to the datastore") 188 } 189 190 numRowsAffected, err := res.RowsAffected() 191 if numRowsAffected == 0 { 192 return errors.New("Failed to add nonce to the datastore; no rows affected") 193 } 194 195 if numRowsAffected != 1 { 196 return errors.Errorf("Expected to affect 1 entry in revocation component info table but affected %d", 197 numRowsAffected) 198 } 199 return err 200 }