Coverage Summary for Class: MerkleTreeUtils (co.rsk.peg.utils)

Class Class, % Method, % Line, %
MerkleTreeUtils 0% (0/1) 0% (0/4) 0% (0/37)


1 package co.rsk.peg.utils; 2  3 import co.rsk.bitcoinj.core.Sha256Hash; 4 import co.rsk.bitcoinj.core.Utils; 5 import co.rsk.bitcoinj.core.VerificationException; 6  7 import static co.rsk.bitcoinj.core.Utils.reverseBytes; 8  9 public class MerkleTreeUtils { 10  // coinbase to check is part of a valid 64 byte tx 11  private static long COINBASE_CHECKED = Utils.readUint32(new byte[]{(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF}, 0); 12  13  private MerkleTreeUtils() {} 14  15  /** 16  * Combines two hashes (representing nodes in a merkle tree) to produce a single hash 17  * that would be the parent of these two nodes. 18  * 19  * @param left The left hand side node bytes 20  * @param right The right hand side node bytes 21  * @return 22  */ 23  public static Sha256Hash combineLeftRight(Sha256Hash left, Sha256Hash right) { 24  byte[] leftBytes = left.getBytes(); 25  byte[] rightBytes = right.getBytes(); 26  checkNotAValid64ByteTransaction(leftBytes, rightBytes); 27  return Sha256Hash.wrapReversed( 28  Sha256Hash.hashTwice( 29  reverseBytes(leftBytes), 0, 32, 30  reverseBytes(rightBytes), 0, 32 31  ) 32  ); 33  } 34  35  36  /** 37  * Checks supplied bytes DO NOT represent a valid bitcoin transaction. 38  * Fixes attack described on https://bitslog.wordpress.com/2018/06/09/leaf-node-weakness-in-bitcoin-merkle-tree-design/ 39  * @throws VerificationException if bytes DO represent a valid bitcoin transaction. 40  */ 41  private static void checkNotAValid64ByteTransaction(byte[] left, byte[] right) { 42  byte[] leftAndRight = new byte[left.length + right.length]; 43  System.arraycopy(left, 0, leftAndRight, 0, 32); 44  System.arraycopy(right, 0, leftAndRight, 32, 32); 45  46  int _offset = 0; 47  48  // Skip version 49  _offset += 4; 50  51  // Check inputs count 52  byte inputCount = leftAndRight[_offset]; 53  _offset += 1; 54  if (inputCount != 1) { 55  // Only 1 input fits in 64 bytes 56  return; 57  } 58  59  // Skip input 0 outpoint hash 60  _offset += 32; 61  62  // Check output 0 index 63  long output0Index = Utils.readUint32(leftAndRight, _offset); 64  _offset += 4; 65  if (output0Index > 1000000 && output0Index != COINBASE_CHECKED){ 66  // this value is capped by max btc tx size 67  // and should check also is not a valid coinbase expressed in a byte array 68  return; 69  } 70  71  // Check input 0 script length 72  byte input0ScriptLength = leftAndRight[_offset]; 73  _offset += 1; 74  if (input0ScriptLength < 0 || input0ScriptLength > 4) { 75  // Script length should be 0 to 4 to create a valid 64 byte tx 76  return; 77  } 78  79  // Skip input 0 script 80  _offset += input0ScriptLength; 81  82  // Skip input 0 sequence 83  _offset += 4; 84  85  // Check output count 86  byte outputCount = leftAndRight[_offset]; 87  _offset += 1; 88  if (outputCount != 1) { 89  // Only 1 output fits in 64 bytes 90  return; 91  } 92  93  // check output 0 value 94  long output0Value = Utils.readInt64(leftAndRight, _offset); 95  _offset += 8; 96  long maxNumberOfSatoshis = 21000000l * 100000000l; 97  if (output0Value < 1 || output0Value > maxNumberOfSatoshis) { 98  // 0 < value < 21 millions (expressed in satoshis) validation failed 99  return; 100  } 101  102  // check output 0 script length 103  byte output0ScriptLength = leftAndRight[_offset]; 104  if (output0ScriptLength < 0 || output0ScriptLength > 4) { 105  // Script length should be 0 to 4 to create a valid 64 byte tx 106  return; 107  } 108  109  // check input 0 script length + output 0 script length 110  if ((input0ScriptLength + output0ScriptLength) != 4) { 111  // Script length should be 0 to 4 112  return; 113  } 114  115  throw new VerificationException("Supplied nodes form a valid btc transaction"); 116  } 117 }