hyperledger / fabric

Hyperledger Fabric is an enterprise-grade permissioned distributed ledger framework for developing solutions and applications. Its modular and versatile design satisfies a broad range of industry use cases. It offers a unique approach to consensus that enables performance at scale while preserving privacy.
https://wiki.hyperledger.org/display/fabric
Apache License 2.0
15.68k stars 8.83k forks source link

Trailing zeros are trimmed after the data is hashed which brakes integrity checks #4160

Open yurii-uhlanov-intellecteu opened 1 year ago

yurii-uhlanov-intellecteu commented 1 year ago

Description

JSON input with trailing zero(s) is being added to a Private Data Collection (PDC):

  1. First, a JSON input with trailing zero(s) is hashed to record the hash on the ledger.
  2. Next, the trailing zeros get trimmed resulting in a modified JSON.
  3. This new value is stored in a PDC.
  4. When retrieved from a PDC, its hash will no longer match the hash of the original input. This brakes integrity checks.

Example:

JSON Input before hashing

{"assetId":"BUG","value":1.400}

JSON stored in PDC

{"assetId":"BUG","value":1.4}

Tested on Fabric 2.3.0 + CouchDB 3.1.1, Fabric 2.5.0 + CouchDB 3.2.2

Steps to reproduce

Prerequisites

To demonstrate the issue, Fabric samples were used.

In the chaincode project:

To start the network and deploy the chaincode, execute:

export PATH=${PWD}/../bin:$PATH
export FABRIC_CFG_PATH=$PWD/../config/
export CORE_PEER_TLS_ENABLED=true
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
export CORE_PEER_MSPCONFIGPATH=${PWD}/organizations/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=localhost:7051

./network.sh up createChannel -ca -s couchdb
./network.sh deployCC  -ccp ../asset-transfer-private-data/chaincode-java -ccl java -ccn private -cccg ../asset-transfer-private-data/chaincode-java/collections_config.json

To reproduce the bug, execute:

peer chaincode invoke -c '{"function":"WriteBug","Args":[]}' -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" -C mychannel -n private --peerAddresses localhost:7051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt"
peer chaincode invoke -c '{"function":"ValidateBug","Args":[]}' -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile "${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem" -C mychannel -n private --peerAddresses localhost:7051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt" --peerAddresses localhost:9051 --tlsRootCertFiles "${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt"

You should see this:

Error: endorsement failure during invoke. response: status:500 message:"Ledger hash is not equal to hash of private data" 
denyeart commented 1 year ago

When writing and re-reading JSON in any platform, the before and after are not guaranteed to match exactly, per the JSON spec. This is true with most JSON parsers, with CouchDB by itself, and when using CouchDB with Fabric.

The behavior is documented here: https://hyperledger-fabric.readthedocs.io/en/latest/couchdb_as_state_database.html#reading-and-writing-json-data

The intended use of GetPrivateDataHash() is to verify that a passed pre-image's hash matches a previously stored pre-image's hash on an organization's peer that doesn't have the private data, not to check against the re-retrieved value on a peer that has the private data, this is described here: https://hyperledger-fabric.readthedocs.io/en/latest/private-data/private-data.html?highlight=getprivatedatahash#sharing-private-data

Therefore this is working as expected and as documented. If you don't think the documentation describes it well enough, this issue can be used to clarify the documentation.

Note that you can accomplish what you want by not saving JSON to CouchDB. Simple string key/value pairs will remain the same when saved and re-read (using either LevelDB or CouchDB).