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  }