Skip to content

Commit

Permalink
feat(RAMF): Limit payload size to 8 MiB per latest change in Relaynet…
Browse files Browse the repository at this point in the history
  • Loading branch information
gnarea committed Feb 14, 2020
1 parent 98b5112 commit c5d3704
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 30 deletions.
113 changes: 85 additions & 28 deletions src/lib/ramf/serialization.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import RAMFValidationError from './RAMFValidationError';
import { deserialize, MessageFields, serialize } from './serialization';

const PAYLOAD = Buffer.from('Hi');
const MAX_PAYLOAD_LENGTH = 2 ** 23 - 1;

const STUB_DATE = new Date(2014, 1, 19);
STUB_DATE.setMilliseconds(0); // There should be tests covering rounding when there are milliseconds
Expand All @@ -39,7 +40,8 @@ const MESSAGE_PARSER = new Parser()
.string('id', { length: 'idLength', encoding: 'ascii' })
.uint32('dateTimestamp')
.buffer('ttlBuffer', { length: 3 })
.uint32('payloadLength')
// @ts-ignore
.buffer('payloadLength', { length: 3, formatter: parse24BitNumber })
.buffer('payload', { length: 'payloadLength' })
.uint16('signatureLength')
.buffer('signature', { length: 'signatureLength' });
Expand Down Expand Up @@ -376,18 +378,52 @@ describe('MessageSerializer', () => {
});
});

test('Payload should be serialized as is', async () => {
const message = new StubMessage(recipientAddress, senderCertificate, PAYLOAD);
describe('Payload', () => {
test('Payload should be serialized as is', async () => {
const largePayload = Buffer.from('a'.repeat(MAX_PAYLOAD_LENGTH));
const message = new StubMessage(recipientAddress, senderCertificate, largePayload);

const messageSerialized = await serialize(
message,
stubConcreteMessageTypeOctet,
stubConcreteMessageVersionOctet,
senderPrivateKey,
);
const messageSerialized = await serialize(
message,
stubConcreteMessageTypeOctet,
stubConcreteMessageVersionOctet,
senderPrivateKey,
);

const messageParts = parseMessage(messageSerialized);
expectBuffersToEqual(messageParts.payload, largePayload);
});

test('Payload length prefix should be serialized as a 23-bit unsigned integer', async () => {
const message = new StubMessage(recipientAddress, senderCertificate, PAYLOAD);

const messageSerialized = await serialize(
message,
stubConcreteMessageTypeOctet,
stubConcreteMessageVersionOctet,
senderPrivateKey,
);

const messageParts = parseMessage(messageSerialized);
expectBuffersToEqual(messageParts.payload, PAYLOAD);
const { payloadLength } = MESSAGE_PARSER.parse(Buffer.from(messageSerialized));
expect(payloadLength).toEqual(PAYLOAD.byteLength);
});

test('Payload size should not exceed 2 ** 23 octets', async () => {
const largePayload = Buffer.from('a'.repeat(MAX_PAYLOAD_LENGTH + 1));
const message = new StubMessage(recipientAddress, senderCertificate, largePayload);
await expectPromiseToReject(
serialize(
message,
stubConcreteMessageTypeOctet,
stubConcreteMessageVersionOctet,
senderPrivateKey,
),
new RAMFSyntaxError(
`Payload size must not exceed ${MAX_PAYLOAD_LENGTH} octets (got ${MAX_PAYLOAD_LENGTH +
1})`,
),
);
});
});

describe('Signature', () => {
Expand Down Expand Up @@ -875,23 +911,42 @@ describe('MessageSerializer', () => {
});
});

test('Payload should be serialized with length prefix', async () => {
const message = new StubMessage(recipientAddress, senderCertificate, PAYLOAD);
const messageSerialized = await serialize(
message,
stubConcreteMessageTypeOctet,
stubConcreteMessageVersionOctet,
senderPrivateKey,
);
describe('Payload', () => {
test('Payload should be serialized with length prefix', async () => {
const message = new StubMessage(recipientAddress, senderCertificate, PAYLOAD);
const messageSerialized = await serialize(
message,
stubConcreteMessageTypeOctet,
stubConcreteMessageVersionOctet,
senderPrivateKey,
);

const messageDeserialized = await deserialize(
messageSerialized,
stubConcreteMessageTypeOctet,
stubConcreteMessageVersionOctet,
StubMessage,
);
const messageDeserialized = await deserialize(
messageSerialized,
stubConcreteMessageTypeOctet,
stubConcreteMessageVersionOctet,
StubMessage,
);

expect(messageDeserialized.payloadSerialized).toEqual(PAYLOAD);
});

expect(messageDeserialized.payloadSerialized).toEqual(PAYLOAD);
test('Payload size should not exceed 2 ** 23 octets', async () => {
const largePayload = Buffer.from('a'.repeat(MAX_PAYLOAD_LENGTH + 1));
const messageSerialized = await serializeWithoutValidation({ payloadBuffer: largePayload });

await expect(
deserialize(
messageSerialized,
stubConcreteMessageTypeOctet,
stubConcreteMessageVersionOctet,
StubMessage,
),
).rejects.toMatchObject<Partial<RAMFValidationError>>({
message: `Payload size must not exceed ${MAX_PAYLOAD_LENGTH} octets (got ${MAX_PAYLOAD_LENGTH +
1})`,
});
});
});

describe('Signature', () => {
Expand Down Expand Up @@ -1065,8 +1120,10 @@ describe('MessageSerializer', () => {
ttlBuffer.writeUIntLE(ttl, 0, 3);
serialization.writeBuffer(ttlBuffer);

serialization.writeUInt32LE(payloadBuffer.byteLength);
serialization.writeBuffer(Buffer.from(payloadBuffer));
const payloadLength = Buffer.allocUnsafe(3);
payloadLength.writeUIntLE(payloadBuffer.byteLength, 0, 3);
serialization.writeBuffer(payloadLength);
serialization.writeBuffer(payloadBuffer);

const finalSignature = Buffer.from(
signature ||
Expand Down
21 changes: 19 additions & 2 deletions src/lib/ramf/serialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const MAX_RECIPIENT_ADDRESS_LENGTH = 2 ** 10 - 1;
const MAX_ID_LENGTH = 2 ** 8 - 1;
const MAX_DATE_TIMESTAMP_SEC = 2 ** 32 - 1;
const MAX_TTL = 2 ** 24 - 1;
const MAX_PAYLOAD_LENGTH = 2 ** 23 - 1;
const MAX_SIGNATURE_LENGTH = 2 ** 14 - 1;

const PARSER = new Parser()
Expand All @@ -24,7 +25,10 @@ const PARSER = new Parser()
.string('id', { length: 'idLength', encoding: 'ascii' })
.uint32('dateTimestamp')
.buffer('ttlBuffer', { length: 3 })
.uint32('payloadLength')
.buffer('payloadLength', {
formatter: (buf: Parser.Data) => (buf as Buffer).readUIntLE(0, 3),
length: 3,
})
.buffer('payload', { length: 'payloadLength' })
.uint16('signatureLength')
.buffer('signature', { length: 'signatureLength' });
Expand Down Expand Up @@ -61,6 +65,7 @@ export async function serialize(
validateMessageIdLength(message.id);
validateDate(message.date);
validateTtl(message.ttl);
validatePayloadLength(message.payloadSerialized);
//endregion

const serialization = new SmartBuffer();
Expand Down Expand Up @@ -93,8 +98,10 @@ export async function serialize(
//endregion

//region Payload
const payloadLength = Buffer.allocUnsafe(3);
payloadLength.writeUIntLE(message.payloadSerialized.byteLength, 0, 3);
const payloadSerialized = Buffer.from(message.payloadSerialized);
serialization.writeUInt32LE(payloadSerialized.byteLength);
serialization.writeBuffer(payloadLength);
serialization.writeBuffer(payloadSerialized);
//endregion

Expand Down Expand Up @@ -132,6 +139,7 @@ export async function deserialize<M extends Message>(

validateFileFormatSignature(messageFields, concreteMessageTypeOctet, concreteMessageVersionOctet);
validateRecipientAddressLength(messageFields.recipientAddress);
validatePayloadLength(messageFields.payload);
validateSignatureLength(messageFields.signature);
//endregion

Expand Down Expand Up @@ -221,6 +229,15 @@ function validateTtl(ttl: number): void {
}
}

function validatePayloadLength(payloadBuffer: ArrayBuffer): void {
const length = payloadBuffer.byteLength;
if (MAX_PAYLOAD_LENGTH < length) {
throw new RAMFSyntaxError(
`Payload size must not exceed ${MAX_PAYLOAD_LENGTH} octets (got ${length})`,
);
}
}

function validateSignatureLength(signatureBuffer: ArrayBuffer): void {
const signatureLength = signatureBuffer.byteLength;
if (MAX_SIGNATURE_LENGTH < signatureLength) {
Expand Down

0 comments on commit c5d3704

Please sign in to comment.