gitee.com/zhaochuninhefei/fabric-ca-gm@v0.0.2/lib/server/idemix/revocationauthority.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 "bytes" 11 "fmt" 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 ecdsa "gitee.com/zhaochuninhefei/gmgo/sm2" 17 log "gitee.com/zhaochuninhefei/zcgolog/zclog" 18 fp256bn "github.com/hyperledger/fabric-amcl/amcl/FP256BN" 19 "github.com/jmoiron/sqlx" 20 "github.com/pkg/errors" 21 ) 22 23 const ( 24 // InsertRAInfo is the SQL for inserting revocation authority info 25 InsertRAInfo = "INSERT into revocation_authority_info(epoch, next_handle, lasthandle_in_pool, level) VALUES (:epoch, :next_handle, :lasthandle_in_pool, :level)" 26 // SelectRAInfo is the query string for getting revocation authority info 27 SelectRAInfo = "SELECT * FROM revocation_authority_info" 28 // UpdateNextAndLastHandle is the SQL for updating next and last revocation handle 29 UpdateNextAndLastHandle = "UPDATE revocation_authority_info SET next_handle = ?, lasthandle_in_pool = ?, epoch = ? WHERE (epoch = ?)" 30 // UpdateNextHandle s the SQL for updating next revocation handle 31 UpdateNextHandle = "UPDATE revocation_authority_info SET next_handle = ? WHERE (epoch = ?)" 32 // DefaultRevocationHandlePoolSize is the default revocation handle pool size 33 DefaultRevocationHandlePoolSize = 1000 34 ) 35 36 // RevocationAuthority is responsible for generating revocation handles and 37 // credential revocation info (CRI) 38 type RevocationAuthority interface { 39 // GetNewRevocationHandle returns new revocation handle, which is required to 40 // create a new Idemix credential 41 GetNewRevocationHandle() (*fp256bn.BIG, error) 42 // CreateCRI returns latest credential revocation information (CRI). CRI contains 43 // information that allows a prover to create a proof that the revocation handle associated 44 // with his credential is not revoked and by the verifier to verify the non-revocation 45 // proof of the prover. Verification will fail if the version of the CRI that verifier has 46 // does not match the version of the CRI that prover used to create non-revocation proof. 47 // The version of the CRI is specified by the Epoch value associated with the CRI. 48 CreateCRI() (*idemix.CredentialRevocationInformation, error) 49 // Epoch returns epoch value of the latest CRI 50 Epoch() (int, error) 51 // PublicKey returns revocation authority's public key 52 PublicKey() *ecdsa.PublicKey 53 } 54 55 // RevocationAuthorityInfo is the revocation authority information record that is 56 // stored in the database 57 type RevocationAuthorityInfo struct { 58 Epoch int `db:"epoch"` 59 NextRevocationHandle int `db:"next_handle"` 60 LastHandleInPool int `db:"lasthandle_in_pool"` 61 Level int `db:"level"` 62 } 63 64 // revocationAuthority implements RevocationComponent interface 65 type revocationAuthority struct { 66 issuer MyIssuer 67 key RevocationKey 68 db db.FabricCADB 69 currentCRI *idemix.CredentialRevocationInformation 70 } 71 72 // NewRevocationAuthority constructor for revocation authority 73 func NewRevocationAuthority(issuer MyIssuer, level int) (RevocationAuthority, error) { 74 ra := &revocationAuthority{ 75 issuer: issuer, 76 db: issuer.DB(), 77 } 78 var err error 79 80 err = ra.initKeyMaterial(false) 81 if err != nil { 82 return nil, err 83 } 84 85 info, err := ra.getRAInfoFromDB() 86 if err != nil { 87 return nil, errors.WithMessage(err, 88 fmt.Sprintf("Failed to initialize revocation authority for issuer '%s'", issuer.Name())) 89 } 90 91 // If epoch is 0, it means this is the first time revocation authority is being 92 // initialized. Initialize revocation authority info and store it in the database 93 if info.Epoch == 0 { 94 rcInfo := RevocationAuthorityInfo{ 95 Epoch: 1, 96 NextRevocationHandle: 1, 97 LastHandleInPool: issuer.Config().RHPoolSize, 98 Level: level, 99 } 100 err = ra.addRAInfoToDB(&rcInfo) 101 if err != nil { 102 return nil, errors.WithMessage(err, 103 fmt.Sprintf("Failed to initialize revocation authority for issuer '%s'", issuer.Name())) 104 } 105 info = &rcInfo 106 } 107 108 return ra, nil 109 } 110 111 func (ra *revocationAuthority) initKeyMaterial(renew bool) error { 112 log.Debug("Initialize Idemix issuer revocation key material") 113 revocationPubKey := ra.issuer.Config().RevocationPublicKeyfile 114 revocationPrivKey := ra.issuer.Config().RevocationPrivateKeyfile 115 rk := NewRevocationKey(revocationPubKey, revocationPrivKey, ra.issuer.IdemixLib()) 116 117 if !renew { 118 pubKeyFileExists := util.FileExists(revocationPubKey) 119 privKeyFileExists := util.FileExists(revocationPrivKey) 120 // If they both exist, the CA was already initialized, load the keys from the disk 121 if pubKeyFileExists && privKeyFileExists { 122 log.Info("The Idemix issuer revocation public and secret key files already exist") 123 log.Infof(" private key file location: %s", revocationPrivKey) 124 log.Infof(" public key file location: %s", revocationPubKey) 125 err := rk.Load() 126 if err != nil { 127 return errors.WithMessage(err, fmt.Sprintf("Failed to load revocation key for issuer '%s'", ra.issuer.Name())) 128 } 129 ra.key = rk 130 return nil 131 } 132 } 133 err := rk.SetNewKey() 134 if err != nil { 135 return errors.WithMessage(err, 136 fmt.Sprintf("Failed to generate revocation key for issuer '%s'", ra.issuer.Name())) 137 } 138 log.Infof("Idemix issuer revocation public and secret keys were generated for CA '%s'", ra.issuer.Name()) 139 err = rk.Store() 140 if err != nil { 141 return errors.WithMessage(err, fmt.Sprintf("Failed to store revocation key of issuer '%s'", ra.issuer.Name())) 142 } 143 ra.key = rk 144 return nil 145 } 146 147 // CreateCRI returns latest credential revocation information (CRI). CRI contains 148 // information that allows a prover to create a proof that the revocation handle associated 149 // with his credential is not revoked and by the verifier to verify the non-revocation 150 // proof of the prover. Verification will fail if the version of the CRI that verifier has 151 // does not match the version of the CRI that prover used to create non-revocation proof. 152 // The version of the CRI is specified by the Epoch value associated with the CRI. 153 func (ra *revocationAuthority) CreateCRI() (*idemix.CredentialRevocationInformation, error) { 154 info, err := ra.getRAInfoFromDB() 155 if err != nil { 156 return nil, errors.WithMessage(err, "Failed to get revocation authority info from datastore") 157 } 158 if ra.currentCRI != nil && ra.currentCRI.Epoch == int64(info.Epoch) { 159 return ra.currentCRI, nil 160 } 161 162 revokedCreds, err := ra.issuer.CredDBAccessor().GetRevokedCredentials() 163 if err != nil { 164 return nil, errors.WithMessage(err, fmt.Sprintf("Failed to get revoked credentials while generating CRI for issuer: '%s'", ra.issuer.Name())) 165 } 166 167 unrevokedHandles := ra.getUnRevokedHandles(info, revokedCreds) 168 169 alg := idemix.ALG_NO_REVOCATION 170 cri, err := ra.issuer.IdemixLib().CreateCRI(ra.key.GetKey(), unrevokedHandles, info.Epoch, alg, ra.issuer.IdemixRand()) 171 if err != nil { 172 return nil, err 173 } 174 ra.currentCRI = cri 175 return ra.currentCRI, nil 176 } 177 178 // GetNewRevocationHandle returns a new revocation handle 179 func (ra *revocationAuthority) GetNewRevocationHandle() (*fp256bn.BIG, error) { 180 h, err := ra.getNextRevocationHandle() 181 if err != nil { 182 return nil, err 183 } 184 rh := fp256bn.NewBIGint(h) 185 return rh, err 186 } 187 188 // Epoch returns epoch value of the latest CRI 189 func (ra *revocationAuthority) Epoch() (int, error) { 190 info, err := ra.getRAInfoFromDB() 191 if err != nil { 192 return 0, errors.WithMessage(err, "Revocation authority failed to get latest epoch") 193 } 194 return info.Epoch, nil 195 } 196 197 // PublicKey returns revocation authority's public key 198 func (ra *revocationAuthority) PublicKey() *ecdsa.PublicKey { 199 return &ra.key.GetKey().PublicKey 200 } 201 202 func (ra *revocationAuthority) getUnRevokedHandles(info *RevocationAuthorityInfo, revokedCreds []CredRecord) []*fp256bn.BIG { 203 log.Debugf("RA '%s' is getting revoked revocation handles for epoch %d", ra.issuer.Name(), info.Epoch) 204 isRevokedHandle := func(rh *fp256bn.BIG) bool { 205 for i := 0; i <= len(revokedCreds)-1; i++ { 206 rrhBytes, err := util.B64Decode(revokedCreds[i].RevocationHandle) 207 if err != nil { 208 log.Debugf("Failed to Base64 decode revocation handle '%s': %s", revokedCreds[i].RevocationHandle, err.Error()) 209 return false 210 } 211 rhBytes := idemix.BigToBytes(rh) 212 if bytes.Equal(rhBytes, rrhBytes) { 213 return true 214 } 215 } 216 return false 217 } 218 validHandles := []*fp256bn.BIG{} 219 for i := 1; i <= info.LastHandleInPool; i = i + 1 { 220 validHandles = append(validHandles, fp256bn.NewBIGint(i)) 221 } 222 for i := len(validHandles) - 1; i >= 0; i-- { 223 isrevoked := isRevokedHandle(validHandles[i]) 224 if isrevoked { 225 validHandles = append(validHandles[:i], validHandles[i+1:]...) 226 } 227 } 228 return validHandles 229 } 230 231 func (ra *revocationAuthority) getRAInfoFromDB() (*RevocationAuthorityInfo, error) { 232 rcinfos := []RevocationAuthorityInfo{} 233 err := ra.db.Select("GetRAInfo", &rcinfos, SelectRAInfo) 234 if err != nil { 235 return nil, err 236 } 237 if len(rcinfos) == 0 { 238 return &RevocationAuthorityInfo{ 239 0, 0, 0, 0, 240 }, nil 241 } 242 return &rcinfos[0], nil 243 } 244 245 func (ra *revocationAuthority) addRAInfoToDB(rcInfo *RevocationAuthorityInfo) error { 246 res, err := ra.db.NamedExec("AddRAInfo", InsertRAInfo, rcInfo) 247 if err != nil { 248 return errors.New("Failed to insert revocation authority info into database") 249 } 250 251 numRowsAffected, err := res.RowsAffected() 252 if numRowsAffected == 0 { 253 return errors.New("Failed to insert the revocation authority info record; no rows affected") 254 } 255 256 if numRowsAffected != 1 { 257 return errors.Errorf("Expected to affect 1 entry in revocation authority info table but affected %d", 258 numRowsAffected) 259 } 260 return err 261 } 262 263 // getNextRevocationHandle returns next revocation handle 264 func (ra *revocationAuthority) getNextRevocationHandle() (int, error) { 265 result, err := doTransaction("GetNextRevocationHandle", ra.db, ra.getNextRevocationHandleTx, nil) 266 if err != nil { 267 return 0, err 268 } 269 270 nextHandle := result.(int) 271 return nextHandle, nil 272 } 273 274 func (ra *revocationAuthority) getNextRevocationHandleTx(tx db.FabricCATx, _ ...interface{}) (interface{}, error) { 275 var err error 276 277 // Get the latest revocation authority info from the database 278 rcInfos := []RevocationAuthorityInfo{} 279 query := SelectRAInfo 280 err = tx.Select("GetRAInfo", &rcInfos, tx.Rebind(query)) 281 if err != nil { 282 return nil, errors.New("Failed to get revocation authority info from database") 283 } 284 if len(rcInfos) == 0 { 285 return nil, errors.New("No revocation authority info found in database") 286 } 287 rcInfo := rcInfos[0] 288 289 nextHandle := rcInfo.NextRevocationHandle 290 newNextHandle := rcInfo.NextRevocationHandle + 1 291 var inQuery string 292 var args []interface{} 293 if nextHandle == rcInfo.LastHandleInPool { 294 newLastHandleInPool := rcInfo.LastHandleInPool + ra.issuer.Config().RHPoolSize 295 newEpoch := rcInfo.Epoch + 1 296 query = UpdateNextAndLastHandle 297 inQuery, args, err = sqlx.In(query, newNextHandle, newLastHandleInPool, newEpoch, rcInfo.Epoch) 298 } else { 299 query = UpdateNextHandle 300 inQuery, args, err = sqlx.In(query, newNextHandle, rcInfo.Epoch) 301 } 302 if err != nil { 303 return nil, errors.Wrapf(err, "Failed to construct query '%s'", query) 304 } 305 _, err = tx.Exec("GetNextRevocationHandle", tx.Rebind(inQuery), args...) 306 if err != nil { 307 return nil, errors.Wrapf(err, "Failed to update revocation authority info") 308 } 309 310 return nextHandle, nil 311 } 312 313 func doTransaction(funcName string, db db.FabricCADB, doit func(tx db.FabricCATx, args ...interface{}) (interface{}, error), args ...interface{}) (interface{}, error) { 314 if db == nil { 315 return nil, errors.New("Failed to correctly setup database connection") 316 } 317 tx := db.BeginTx() 318 result, err := doit(tx, args...) 319 if err != nil { 320 err2 := tx.Rollback(funcName) 321 if err2 != nil { 322 errMsg := fmt.Sprintf("Error encountered while rolling back transaction: %s, original error: %s", err2.Error(), err.Error()) 323 log.Errorf(errMsg) 324 return nil, errors.New(errMsg) 325 } 326 return nil, err 327 } 328 329 err = tx.Commit(funcName) 330 if err != nil { 331 return nil, errors.Wrap(err, "Error encountered while committing transaction") 332 } 333 334 return result, nil 335 }