Skip to content

Commit

Permalink
feat: Introduce Cargo message
Browse files Browse the repository at this point in the history
  • Loading branch information
gnarea committed Feb 13, 2020
1 parent 45d4798 commit 592f53a
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 108 deletions.
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,6 @@ export { default as RAMFSyntaxError } from './lib/ramf/RAMFSyntaxError';
export { default as RAMFValidationError } from './lib/ramf/RAMFValidationError';
export { default as InvalidMessageError } from './lib/messages/InvalidMessageError';
export { default as Parcel } from './lib/messages/Parcel';
export { default as Cargo } from './lib/messages/Cargo';

//endregion
6 changes: 6 additions & 0 deletions src/lib/messages/Cargo.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { describeMessage } from './_test_utils';
import Cargo from './Cargo';

describe('Cargo', () => {
describeMessage(Cargo, 0x43, 0x0);
});
32 changes: 32 additions & 0 deletions src/lib/messages/Cargo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// This module wouldn't duplicate Parcel.ts if TypeScript supported static+abstract methods

import { SignatureOptions } from '../crypto_wrappers/cms/signedData';
import * as serialization from '../ramf/serialization';
import Message from './Message';

const concreteMessageTypeOctet = 0x43;
const concreteMessageVersionOctet = 0;

export default class Cargo extends Message {
public static async deserialize(parcelSerialized: ArrayBuffer): Promise<Cargo> {
return serialization.deserialize(
parcelSerialized,
concreteMessageTypeOctet,
concreteMessageVersionOctet,
Cargo,
);
}

public async serialize(
senderPrivateKey: CryptoKey,
signatureOptions?: SignatureOptions,
): Promise<ArrayBuffer> {
return serialization.serialize(
this,
concreteMessageTypeOctet,
concreteMessageVersionOctet,
senderPrivateKey,
signatureOptions,
);
}
}
110 changes: 2 additions & 108 deletions src/lib/messages/Parcel.spec.ts
Original file line number Diff line number Diff line change
@@ -1,112 +1,6 @@
/* tslint:disable:no-let */
import bufferToArray from 'buffer-to-arraybuffer';

import { generateStubCert, getMockContext } from '../_test_utils';
import { generateRSAKeyPair } from '../crypto_wrappers/keys';
import * as serialization from '../ramf/serialization';
import { describeMessage } from './_test_utils';
import Parcel from './Parcel';

afterAll(() => {
jest.restoreAllMocks();
});

describe('Parcel', () => {
let parcel: Parcel;
let senderPrivateKey: CryptoKey;
beforeAll(async () => {
const senderKeyPair = await generateRSAKeyPair();
const senderCertificate = await generateStubCert({
issuerPrivateKey: senderKeyPair.privateKey,
});
senderPrivateKey = senderKeyPair.privateKey;
parcel = new Parcel('address', senderCertificate, Buffer.from('hi'));
});

describe('serialize', () => {
const expectedSerialization = bufferToArray(Buffer.from('serialized'));
const serializeSpy = jest.spyOn(serialization, 'serialize');
beforeAll(() => {
serializeSpy.mockResolvedValueOnce(expectedSerialization);
});
afterEach(() => {
serializeSpy.mockReset();
});

test('Result should be RAMF serialization', async () => {
const messageSerialized = await parcel.serialize(senderPrivateKey);

expect(serializeSpy).toBeCalledTimes(1);
expect(messageSerialized).toBe(expectedSerialization);
});

test('Concrete message type should be 0x50', async () => {
await parcel.serialize(senderPrivateKey);

const serializeCallArs = getMockContext(serialization.serialize).calls[0];
expect(serializeCallArs[1]).toEqual(0x50);
});

test('Concrete message version should be 0x0', async () => {
await parcel.serialize(senderPrivateKey);

const serializeCallArs = getMockContext(serialization.serialize).calls[0];
expect(serializeCallArs[2]).toEqual(0);
});

test('Message should be signed with private key specified', async () => {
await parcel.serialize(senderPrivateKey);

const serializeCallArs = getMockContext(serialization.serialize).calls[0];
expect(serializeCallArs[3]).toEqual(senderPrivateKey);
});

test('Signature options should be honored', async () => {
const signatureOptions = { hashingAlgorithmName: 'SHA-384' };
await parcel.serialize(senderPrivateKey, signatureOptions);

const serializeCallArs = getMockContext(serialization.serialize).calls[0];
expect(serializeCallArs[4]).toEqual(signatureOptions);
});
});

describe('deserialize', () => {
const stubParcelSerialized = bufferToArray(Buffer.from('I am a parcel. I swear.'));
const deserializeSpy = jest.spyOn(serialization, 'deserialize');
beforeAll(() => {
deserializeSpy.mockResolvedValueOnce(parcel);
});
afterEach(() => {
deserializeSpy.mockReset();
});

test('Result should be the parcel', async () => {
const parcelDeserialized = await Parcel.deserialize(stubParcelSerialized);

expect(parcelDeserialized).toBe(parcel);
expect(deserializeSpy).toBeCalledTimes(1);
const deserializeCallArgs = getMockContext(deserializeSpy).calls[0];
expect(deserializeCallArgs[0]).toBe(stubParcelSerialized);
});

test('Concrete message type should be 0x50', async () => {
await Parcel.deserialize(stubParcelSerialized);

const deserializeCallArgs = getMockContext(deserializeSpy).calls[0];
expect(deserializeCallArgs[1]).toEqual(0x50);
});

test('Concrete message version should be 0x0', async () => {
await Parcel.deserialize(stubParcelSerialized);

const deserializeCallArgs = getMockContext(deserializeSpy).calls[0];
expect(deserializeCallArgs[2]).toEqual(0);
});

test('Message class should be Parcel', async () => {
await Parcel.deserialize(stubParcelSerialized);

const deserializeCallArgs = getMockContext(deserializeSpy).calls[0];
expect(deserializeCallArgs[3]).toBe(Parcel);
});
});
describeMessage(Parcel, 0x50, 0x0);
});
2 changes: 2 additions & 0 deletions src/lib/messages/Parcel.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// This module wouldn't duplicate Cargo.ts if TypeScript supported static+abstract methods

import { SignatureOptions } from '../crypto_wrappers/cms/signedData';
import * as serialization from '../ramf/serialization';
import Message from './Message';
Expand Down
123 changes: 123 additions & 0 deletions src/lib/messages/_test_utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/* tslint:disable:no-let */
import bufferToArray from 'buffer-to-arraybuffer';

import { generateStubCert, getMockContext } from '../_test_utils';
import { generateRSAKeyPair } from '../crypto_wrappers/keys';
import * as serialization from '../ramf/serialization';
import Message from './Message';

interface MessageClass<M extends Message> {
readonly deserialize: (serialization: ArrayBuffer) => Promise<M>;
// tslint:disable-next-line:no-mixed-interface
new (...args: readonly any[]): M;
}

export function describeMessage<M extends Message>(
messageClass: MessageClass<M>,
messageType: number,
messageVersion: number,
): void {
afterAll(() => {
jest.restoreAllMocks();
});

let message: M;
let senderPrivateKey: CryptoKey;

beforeAll(async () => {
const senderKeyPair = await generateRSAKeyPair();
const senderCertificate = await generateStubCert({
issuerPrivateKey: senderKeyPair.privateKey,
});
senderPrivateKey = senderKeyPair.privateKey;
message = new messageClass('address', senderCertificate, Buffer.from('hi'));
});

describe('serialize', () => {
const expectedSerialization = bufferToArray(Buffer.from('serialized'));
const serializeSpy = jest.spyOn(serialization, 'serialize');
beforeAll(() => {
serializeSpy.mockResolvedValueOnce(expectedSerialization);
});
afterEach(() => {
serializeSpy.mockReset();
});

test('Result should be RAMF serialization', async () => {
const messageSerialized = await message.serialize(senderPrivateKey);

expect(serializeSpy).toBeCalledTimes(1);
expect(messageSerialized).toBe(expectedSerialization);
});

test(`Concrete message type should be ${messageType}`, async () => {
await message.serialize(senderPrivateKey);

const serializeCallArs = getMockContext(serialization.serialize).calls[0];
expect(serializeCallArs[1]).toEqual(messageType);
});

test(`Concrete message version should be ${messageVersion}`, async () => {
await message.serialize(senderPrivateKey);

const serializeCallArs = getMockContext(serialization.serialize).calls[0];
expect(serializeCallArs[2]).toEqual(messageVersion);
});

test('Message should be signed with private key specified', async () => {
await message.serialize(senderPrivateKey);

const serializeCallArs = getMockContext(serialization.serialize).calls[0];
expect(serializeCallArs[3]).toEqual(senderPrivateKey);
});

test('Signature options should be honored', async () => {
const signatureOptions = { hashingAlgorithmName: 'SHA-384' };
await message.serialize(senderPrivateKey, signatureOptions);

const serializeCallArs = getMockContext(serialization.serialize).calls[0];
expect(serializeCallArs[4]).toEqual(signatureOptions);
});
});

describe('deserialize', () => {
const stubMessageSerialized = bufferToArray(Buffer.from('I am a message. I swear.'));
const deserializeSpy = jest.spyOn(serialization, 'deserialize');
beforeAll(() => {
deserializeSpy.mockResolvedValueOnce(message);
});
afterEach(() => {
deserializeSpy.mockReset();
});

test('Result should be the expected message', async () => {
const messageDeserialized = await messageClass.deserialize(stubMessageSerialized);

expect(messageDeserialized).toBe(message);
expect(deserializeSpy).toBeCalledTimes(1);
const deserializeCallArgs = getMockContext(deserializeSpy).calls[0];
expect(deserializeCallArgs[0]).toBe(stubMessageSerialized);
});

test(`Concrete message type should be ${messageType}`, async () => {
await messageClass.deserialize(stubMessageSerialized);

const deserializeCallArgs = getMockContext(deserializeSpy).calls[0];
expect(deserializeCallArgs[1]).toEqual(messageType);
});

test(`Concrete message version should be ${messageVersion}`, async () => {
await messageClass.deserialize(stubMessageSerialized);

const deserializeCallArgs = getMockContext(deserializeSpy).calls[0];
expect(deserializeCallArgs[2]).toEqual(messageVersion);
});

test(`Message class should be ${messageClass.name}`, async () => {
await messageClass.deserialize(stubMessageSerialized);

const deserializeCallArgs = getMockContext(deserializeSpy).calls[0];
expect(deserializeCallArgs[3]).toBe(messageClass);
});
});
}

0 comments on commit 592f53a

Please sign in to comment.