Skip to content

Commit

Permalink
Ensure invalid RLPs cannot be decoded (#101)
Browse files Browse the repository at this point in the history
* rlp: add invalid tests from ethereum/tests

* rlp: remove empty string as invalid input

* rlp: additional error checks

* tidy tests

* improve safeSlice error message
return statement in else block can be placed outside if statement (https://eslint.org/docs/rules/no-else-return)

* tidy

* rlp: add cases from Geth

* rlp: tests: cleanup

* rlp: tests: fix description

* rlp: tests: create invalid test file

* rlp: dataTypes: increase getLength coverage

* rlp: check and remove uncovered lines

* Apply suggestions from code review

two nits

* clarify error messages

* Update test/dataTypes.spec.ts

Co-authored-by: acolytec3 <17355484+acolytec3@users.noreply.github.com>

* Update test/official.spec.ts

Co-authored-by: acolytec3 <17355484+acolytec3@users.noreply.github.com>

* lint

* fix one more deprecated equal

* tests: change deprecated methods to their new variants

Co-authored-by: Ryan Ghods <ryan@ryanio.com>
Co-authored-by: acolytec3 <17355484+acolytec3@users.noreply.github.com>
  • Loading branch information
3 people authored Dec 28, 2021
1 parent efe7073 commit ced5ef2
Show file tree
Hide file tree
Showing 6 changed files with 507 additions and 90 deletions.
40 changes: 26 additions & 14 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ export function encode(input: Input): Buffer {
}
}

/**
* Slices a Buffer, throws if the slice goes out-of-bounds of the Buffer
* E.g. safeSlice(Buffer.from('aa', 'hex'), 1, 2) will throw.
* @param input
* @param start
* @param end
*/
function safeSlice(input: Buffer, start: number, end: number) {
if (end > input.length) {
throw new Error('invalid RLP (safeSlice): end slice of Buffer out-of-bounds')
}
return input.slice(start, end)
}

/**
* Parse integers. Check if there is no leading zeros
* @param v The value to parse
Expand Down Expand Up @@ -103,7 +117,7 @@ export function getLength(input: Input): Buffer | number {
} else {
// a list over 55 bytes long
const llength = firstByte - 0xf6
const length = safeParseInt(inputBuffer.slice(1, llength).toString('hex'), 16)
const length = safeParseInt(safeSlice(inputBuffer, 1, llength).toString('hex'), 16)
return llength + length
}
}
Expand All @@ -129,11 +143,11 @@ function _decode(input: Buffer): Decoded {
if (firstByte === 0x80) {
data = Buffer.from([])
} else {
data = input.slice(1, length)
data = safeSlice(input, 1, length)
}

if (length === 2 && data[0] < 0x80) {
throw new Error('invalid rlp encoding: byte must be less 0x80')
throw new Error('invalid rlp encoding: invalid prefix, single byte < 0x80 are not prefixed')
}

return {
Expand All @@ -147,14 +161,11 @@ function _decode(input: Buffer): Decoded {
if (input.length - 1 < llength) {
throw new Error('invalid RLP: not enough bytes for string length')
}
length = safeParseInt(input.slice(1, llength).toString('hex'), 16)
length = safeParseInt(safeSlice(input, 1, llength).toString('hex'), 16)
if (length <= 55) {
throw new Error('invalid RLP: expected string length to be greater than 55')
}
data = input.slice(llength, length + llength)
if (data.length < length) {
throw new Error('invalid RLP: not enough bytes for string')
}
data = safeSlice(input, llength, length + llength)

return {
data: data,
Expand All @@ -163,7 +174,7 @@ function _decode(input: Buffer): Decoded {
} else if (firstByte <= 0xf7) {
// a list between 0-55 bytes long
length = firstByte - 0xbf
innerRemainder = input.slice(1, length)
innerRemainder = safeSlice(input, 1, length)
while (innerRemainder.length) {
d = _decode(innerRemainder)
decoded.push(d.data as Buffer)
Expand All @@ -177,22 +188,23 @@ function _decode(input: Buffer): Decoded {
} else {
// a list over 55 bytes long
llength = firstByte - 0xf6
length = safeParseInt(input.slice(1, llength).toString('hex'), 16)
length = safeParseInt(safeSlice(input, 1, llength).toString('hex'), 16)
if (length < 56) {
throw new Error('invalid RLP: encoded list too short')
}
const totalLength = llength + length
if (totalLength > input.length) {
throw new Error('invalid rlp: total length is larger than the data')
}

innerRemainder = input.slice(llength, totalLength)
if (innerRemainder.length === 0) {
throw new Error('invalid rlp, List has a invalid length')
}
innerRemainder = safeSlice(input, llength, totalLength)

while (innerRemainder.length) {
d = _decode(innerRemainder)
decoded.push(d.data as Buffer)
innerRemainder = d.remainder
}

return {
data: decoded,
remainder: input.slice(totalLength),
Expand Down
Loading

0 comments on commit ced5ef2

Please sign in to comment.