diff --git a/contracts/contracts/CBOR.sol b/contracts/contracts/CBOR.sol index 51bab564..24b9812b 100644 --- a/contracts/contracts/CBOR.sol +++ b/contracts/contracts/CBOR.sol @@ -3,28 +3,35 @@ pragma solidity ^0.8.0; /// While parsing CBOR map, unexpected key -error CBOR_Error_InvalidKey(); +error CBOR_InvalidKey(); /// While parsing CBOR map, length is invalid, or other parse error -error CBOR_Error_InvalidMap(); +error CBOR_InvalidMap(); /// While parsing CBOR structure, data length was unexpected -error CBOR_Error_InvalidLength(uint256); +error CBOR_InvalidLength(uint256); /// Value cannot be parsed as a uint -error CBOR_Error_InvalidUintPrefix(uint8); +error CBOR_InvalidUintPrefix(uint8); -/// Unsigned integer of unknown size -error CBOR_Error_InvalidUintSize(uint8); +/// CBOR spec supports, 1, 2, 4 & 8 byte uints. Caused by parse error. +error CBOR_InvalidUintSize(uint8); /// CBOR parsed value is out of expected range -error CBOR_Error_ValueOutOfRange(); +error CBOR_Error_ValueOutOfRange(uint256 value, uint256 maxValue); +/// Buffer too short to parse expected value +error CBOR_Error_BufferOverrun(uint256 len, uint256 offset, uint256 need); + +/// @notice Byte array is too long +/// @dev Solidity has 256bit length prefixes for byte arrays. We decided for +/// a reasonable cutoff point (64kb), so at most a 2-byte uint describing +/// the array length. error CBOR_Error_BytesTooLong(uint256 byteLength); +/// @dev we don't follow bignum tagged encoding +/// See: https://www.rfc-editor.org/rfc/rfc8949.html#section-3.4.3 function encodeUint(uint256 value) pure returns (bytes memory) { - // NOTE: we don't follow bignum tagged encoding - // See: https://www.rfc-editor.org/rfc/rfc8949.html#section-3.4.3 if (value < 24) { return abi.encodePacked(uint8(value)); } else if (value <= type(uint8).max) { @@ -71,7 +78,7 @@ function parseMapStart(bytes memory in_data, uint256 in_offset) { uint256 b = uint256(uint8(in_data[in_offset])); if (b < 0xa0 || b > 0xb7) { - revert CBOR_Error_InvalidMap(); + revert CBOR_InvalidMap(); } n_entries = b - 0xa0; @@ -108,16 +115,21 @@ function parseUint(bytes memory result, uint256 offset) } else if (prefix == 0x1b) { len = 8; } else { - revert CBOR_Error_InvalidUintSize(prefix); + // Falls outside of the CBOR spec, tagged as uint, but unsupported + revert CBOR_InvalidUintSize(prefix); } offset += 1; } // Unknown... else { - revert CBOR_Error_InvalidUintPrefix(prefix); + revert CBOR_InvalidUintPrefix(prefix); } - if (len > 0x20) revert CBOR_Error_InvalidLength(len); + // Exceeds what can be represented by a 256bit word + if (len > 0x20) revert CBOR_InvalidLength(len); + + if (offset + len > result.length) + revert CBOR_Error_BufferOverrun(result.length, offset, len); // Load 32 bytes from the buffer at the given offset assembly { @@ -138,7 +150,8 @@ function parseUint64(bytes memory result, uint256 offset) (newOffset, tmp) = parseUint(result, offset); - if (tmp > type(uint64).max) revert CBOR_Error_ValueOutOfRange(); + if (tmp > type(uint64).max) + revert CBOR_Error_ValueOutOfRange(tmp, type(uint64).max); value = uint64(tmp); } @@ -151,7 +164,8 @@ function parseUint128(bytes memory result, uint256 offset) (newOffset, tmp) = parseUint(result, offset); - if (tmp > type(uint128).max) revert CBOR_Error_ValueOutOfRange(); + if (tmp > type(uint128).max) + revert CBOR_Error_ValueOutOfRange(tmp, type(uint128).max); value = uint128(tmp); } @@ -160,7 +174,7 @@ function parseKey(bytes memory result, uint256 offset) pure returns (uint256 newOffset, bytes32 keyDigest) { - if (result[offset] & 0x60 != 0x60) revert CBOR_Error_InvalidKey(); + if (result[offset] & 0x60 != 0x60) revert CBOR_InvalidKey(); uint8 len = uint8(result[offset++]) ^ 0x60; diff --git a/contracts/contracts/Subcall.sol b/contracts/contracts/Subcall.sol index 924d62f8..d0917aa5 100644 --- a/contracts/contracts/Subcall.sol +++ b/contracts/contracts/Subcall.sol @@ -51,6 +51,12 @@ library Subcall { error AccountsTransferError(uint64 status, string data); + /// Expected map of different size! + error WrongMapSizeError(); + + /// Unknown type of receipt! + error TakeReceiptKindOutOfRange(uint receiptKind); + /// The origin is not authorized for the given ROFL app error RoflOriginNotAuthorizedForApp(); @@ -63,6 +69,9 @@ library Subcall { /// CBOR parser expected a key, but it was not found in the map! error MissingKey(); + /// We expected to have parsed everything, but there are excess bytes! + error IncompleteParse(); + /// Error while trying to retrieve current epoch error CoreCurrentEpochError(uint64); @@ -176,7 +185,7 @@ library Subcall { if (receiptId == 0) revert InvalidReceiptId(); if (uint256(kind) == 0 || uint256(kind) > 23) - revert CBOR.CBOR_Error_ValueOutOfRange(); + revert TakeReceiptKindOutOfRange(uint256(kind)); (bool success, bytes memory data) = SUBCALL.call( // solhint-disable-line abi.encode( @@ -224,7 +233,8 @@ library Subcall { bool hasReceipt = false; - if (result[0] != 0xA2) revert CBOR.CBOR_Error_InvalidMap(); + // Expects map with 2 pairs + if (result[0] != 0xA2) revert WrongMapSizeError(); while (offset < result.length) { bytes32 keyDigest; @@ -254,7 +264,8 @@ library Subcall { bool hasAmount = false; - if (result[0] != 0xA1) revert CBOR.CBOR_Error_InvalidMap(); + // Expects map with 1 pair + if (result[0] != 0xA1) revert WrongMapSizeError(); while (offset < result.length) { bytes32 keyDigest; @@ -281,19 +292,15 @@ library Subcall { pure returns (uint128 shares) { - if (result[0] != 0xA1) revert CBOR.CBOR_Error_InvalidMap(); + // Expects map with 1 pair + if (result[0] != 0xA1) revert WrongMapSizeError(); if (result[0] == 0xA1 && result[1] == 0x66 && result[2] == "s") { // Delegation succeeded, decode number of shares. - uint8 sharesLen = uint8(result[8]) & 0x1f; // Assume shares field is never greater than 16 bytes. - - if (9 + sharesLen != result.length) - revert CBOR.CBOR_Error_InvalidLength(sharesLen); - - for (uint256 offset = 0; offset < sharesLen; offset++) { - uint8 v = uint8(result[9 + offset]); - - shares += uint128(v) << (8 * uint128(sharesLen - offset - 1)); + uint newOffset; + (newOffset,shares) = CBOR.parseUint128(result, 8); + if( newOffset != result.length ) { + revert IncompleteParse(); } } else { revert ParseReceiptError(receiptId); @@ -566,12 +573,12 @@ library Subcall { ); } else if (keyDigest == keccak256("signature")) { if (in_data[offset++] != 0x58) { - revert CBOR.CBOR_Error_InvalidUintPrefix( + revert CBOR.CBOR_InvalidUintPrefix( uint8(in_data[offset - 1]) ); } if (in_data[offset++] != 0x40) { - revert CBOR.CBOR_Error_InvalidUintSize( + revert CBOR.CBOR_InvalidUintSize( uint8(in_data[offset - 1]) ); } @@ -587,7 +594,7 @@ library Subcall { offset += 0x40; } else { - revert CBOR.CBOR_Error_InvalidKey(); + revert CBOR.CBOR_InvalidKey(); } } } @@ -614,7 +621,7 @@ library Subcall { offset ); } else { - revert CBOR.CBOR_Error_InvalidKey(); + revert CBOR.CBOR_InvalidKey(); } } }