Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions packages/sdk/src/__test__/unit/encryptedResponses.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import {
LatticeSecureEncryptedRequestType,
ProtocolConstants,
encryptedSecureRequest,
} from '../../protocol';
import { aes256_encrypt, checksum, getP256KeyPair } from '../../util';
import { request } from '../../shared/functions';

vi.mock('../../shared/functions', async () => {
const actual = await vi.importActual<typeof import('../../shared/functions')>(
'../../shared/functions',
);
return {
...actual,
request: vi.fn(),
};
});

const requestMock = vi.mocked(request);

const buildEncryptedResponse = ({
sharedSecret,
requestType,
status,
responsePub,
}: {
sharedSecret: Buffer;
requestType: LatticeSecureEncryptedRequestType;
status: number;
responsePub: Buffer;
}) => {
const responseDataSize =
ProtocolConstants.msgSizes.secure.data.response.encrypted[requestType];
const decrypted = Buffer.alloc(
ProtocolConstants.msgSizes.secure.data.response.encrypted.encryptedData,
);
responsePub.copy(decrypted, 0);
decrypted[responsePub.length] = status;
const checksumOffset = responsePub.length + responseDataSize;
const cs = checksum(decrypted.slice(0, checksumOffset));
decrypted.writeUInt32BE(cs, checksumOffset);
return aes256_encrypt(decrypted, sharedSecret);
};

describe('encryptedSecureRequest response sizes', () => {
const requestType = LatticeSecureEncryptedRequestType.event;
const sharedSecret = Buffer.alloc(32, 7);
const ephemeralPub = getP256KeyPair(Buffer.alloc(32, 3));
const requestData = Buffer.alloc(
ProtocolConstants.msgSizes.secure.data.request.encrypted[requestType],
);
const responseKey = getP256KeyPair(Buffer.alloc(32, 9));
const responsePub = Buffer.from(
responseKey.getPublic().encode('hex', false),
'hex',
);

beforeEach(() => {
requestMock.mockReset();
});

it('accepts compact encrypted response size', async () => {
const encryptedResponse = buildEncryptedResponse({
sharedSecret,
requestType,
status: 0,
responsePub,
});
requestMock.mockResolvedValueOnce(encryptedResponse);

const result = await encryptedSecureRequest({
data: requestData,
requestType,
sharedSecret,
ephemeralPub,
url: 'http://example.test',
});

expect(result.decryptedData[0]).toBe(0);
});

it('accepts legacy encrypted response size', async () => {
const encryptedResponse = buildEncryptedResponse({
sharedSecret,
requestType,
status: 0,
responsePub,
});
const legacyResponse = Buffer.concat([
encryptedResponse,
Buffer.alloc(encryptedResponse.length),
]);
requestMock.mockResolvedValueOnce(legacyResponse);

const result = await encryptedSecureRequest({
data: requestData,
requestType,
sharedSecret,
ephemeralPub,
url: 'http://example.test',
});

expect(result.decryptedData[0]).toBe(0);
});
});
60 changes: 60 additions & 0 deletions packages/sdk/src/__test__/unit/sendEvent.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { __private__ as sendEventPrivate } from '../../functions/sendEvent';

const { encodeEventPayload } = sendEventPrivate;

describe('sendEvent encoding', () => {
it('encodes and pads message payload', () => {
const payload = encodeEventPayload({
eventType: 1,
eventId: '00000000-0000-0000-0000-000000000000',
message: 'hi',
});
expect(payload.length).toBe(1722);
expect(payload[0]).toBe(1);
expect(payload.slice(1, 17).every((b) => b === 0)).toBe(true);
expect(payload.readUInt16LE(17)).toBe(2);
expect(payload.slice(19, 21).toString('utf8')).toBe('hi');
expect(payload.slice(21).every((b) => b === 0)).toBe(true);
});

it('throws on empty message', () => {
expect(() =>
encodeEventPayload({
eventType: 1,
eventId: '00000000-0000-0000-0000-000000000000',
message: '',
}),
).toThrow(/must not be empty/i);
});

it('throws when message is too long', () => {
const longMsg = 'a'.repeat(1704);
expect(() =>
encodeEventPayload({
eventType: 1,
eventId: '00000000-0000-0000-0000-000000000000',
message: longMsg,
}),
).toThrow(/too long/i);
});

it('throws on invalid eventId', () => {
expect(() =>
encodeEventPayload({
eventType: 1,
eventId: 'not-a-uuid',
message: 'hi',
}),
).toThrow(/eventId/i);
});

it('throws on invalid eventType', () => {
expect(() =>
encodeEventPayload({
eventType: 256,
eventId: '00000000-0000-0000-0000-000000000000',
message: 'hi',
}),
).toThrow(/eventType/i);
});
});
Loading
Loading