Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: custom length encoding and decoding #8

Merged
merged 6 commits into from
Nov 13, 2019

Conversation

alanshaw
Copy link
Owner

@alanshaw alanshaw commented Nov 13, 2019

This PR allows a custom length encoding/decoding function to be passed to encode/decode.

There's also a int32BE fixed length encoder/decoder. e.g.

const lp = require('it-length-prefixed')
const { int32BEEncode, int32BEDecode } = require('it-length-prefixed')

await pipe(
  [Buffer.from('hello world')],
  lp.encode({ lengthEncoder: int32BEEncode }),
  lp.decode({ lengthDecoder: int32BEDecode }),
  async source => {
    for await (const chunk of source) {
      console.log(chunk.toString())
    }
  }
)

See updated README for more info.

resolves #5

BREAKING CHANGE: Additional validation now checks for messages with a length that is too long to prevent a possible DoS attack. The error code ERR_MSG_TOO_LONG has changed to ERR_MSG_DATA_TOO_LONG and the error code ERR_MSG_LENGTH_TOO_LONG has been added.

Alan Shaw added 5 commits November 12, 2019 15:55
License: MIT
Signed-off-by: Alan Shaw <alan.shaw@protocol.ai>
This PR allows a custom length encoding/decoding function to be passed to `encode`/`decode`.

There's also a int32BE fixed length encoder/decoder. e.g.

```js
const lp = require('it-length-prefixed')
const { int32Encode, int32Decode } = require('it-length-prefixed')

await pipe(
  [Buffer.from('hello world')],
  lp.encode({ lengthEncoder: int32Encode }),
  lp.decode({ lengthDecoder: int32Decode }),
  async source => {
    for await (const chunk of source) {
      console.log(chunk.toString())
    }
  }
)
```

See updated README for more info.

BREAKING CHANGE: Additional validation now checks for messages with a length that is too long to prevent a possible DoS attack. The error code `ERR_MSG_TOO_LONG` has changed to `ERR_MSG_DATA_TOO_LONG` and the error code `ERR_MSG_LENGTH_TOO_LONG` has been added.

License: MIT
Signed-off-by: Alan Shaw <alan.shaw@protocol.ai>
if (dataLength > options.maxDataLength) {
throw Object.assign(new Error('message too long'), { code: 'ERR_MSG_TOO_LONG' })
throw Object.assign(new Error('message data too long'), { code: 'ERR_MSG_DATA_TOO_LONG' })
Copy link
Owner Author

@alanshaw alanshaw Nov 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please note this change! I noticed that while reading message length an attacker could send Buffers of 0x80 (128 or greater) and we're continue buffering it up indefinitely until we run out of memory. I believe this is possible in pull-length-prefixed also.

it('should not decode message length that is too long', async () => {
// A value < 0x80 signifies end of varint so pass buffers of >= 0x80
// so that it will keep throwing a RangeError until we reach the max length
const lengths = times(randomInt(5, 10), () => Buffer.alloc(MAX_LENGTH_LENGTH / 4).fill(0x80))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is randomizing the times executed here needed? This should fail consistently on the 5th attempt correct?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should do yes, will switch to 5.

README.md Outdated
@@ -61,23 +61,31 @@ console.log(decoded)

- `opts: Object`, optional
- `poolSize: 10 * 1024`: Buffer pool size to allocate up front
- `minPoolSize: 147`: The minimum size the pool can be before it is re-allocated. Note this is important
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note this is important

This ties directly to ones ability to read the length of the data correct? In decode.js the comment signifies this is equivalent to the number of bytes needed to read Varint.encode(Number.MAX_VALUE).length (147), Number.MAX_VALUE is a float though. Should it be Varint.encode(Number.MAX_SAFE_INTEGER).length (8)?

Copy link
Owner Author

@alanshaw alanshaw Nov 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When allocating a buffer the docs say "<integer>" so I assume then the biggest you can allocate would be Number.MAX_SAFE_INTEGER meaning yes, you're right this should be 8 bytes, not 147. Good catch.

Copy link
Collaborator

@jacobheun jacobheun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 looks good

@alanshaw alanshaw merged commit e419b63 into master Nov 13, 2019
@alanshaw alanshaw deleted the feat/custom-length-encoding-decoding branch November 13, 2019 14:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Fixed length length-prefix
2 participants