Page MenuHomePhabricator

D7061.id23693.diff
No OneTemporary

D7061.id23693.diff

diff --git a/lib/utils/pkcs7-padding.js b/lib/utils/pkcs7-padding.js
--- a/lib/utils/pkcs7-padding.js
+++ b/lib/utils/pkcs7-padding.js
@@ -16,6 +16,56 @@
import invariant from 'invariant';
+export type PaddingConfiguration = {
+ +blockSizeBytes: number,
+ +superblockSizeBlocks: number,
+};
+
+/**
+ * The padding configuration for 10KB superblocks.
+ * The block size is 250 bytes, and the superblock size is 40 blocks.
+ */
+export const PKCS7_10KB: PaddingConfiguration = {
+ blockSizeBytes: 250,
+ superblockSizeBlocks: 40,
+};
+
+/**
+ * Pads the input using the extended PKCS#7 padding (superblock padding).
+ * The input is first padded using the standard PKCS#7 padding, and then
+ * padded to the nearest multiple of superblocks (blocks of blocks).
+ *
+ * @param {Uint8Array} data - The input to be padded.
+ * @param {PaddingConfiguration} paddingConfiguration - The padding
+ * configuration. Defaults to multiple of 10KB. See {@link PKCS7_10KB}.
+ * @returns {Uint8Array} The padded input.
+ */
+export function pad(
+ data: Uint8Array,
+ { blockSizeBytes, superblockSizeBlocks }: PaddingConfiguration = PKCS7_10KB,
+): Uint8Array {
+ const pkcs7Padded = pkcs7pad(data, blockSizeBytes);
+ return superblockPad(pkcs7Padded, blockSizeBytes, superblockSizeBlocks);
+}
+
+/**
+ * Unpads the input using the extended PKCS#7 padding (superblock padding).
+ * The input is first unpadded on the block basis, and then unpadded using
+ * the standard PKCS#7 padding.
+ *
+ * @param {Uint8Array} data - The input to be unpadded.
+ * @param {PaddingConfiguration} paddingConfiguration - The padding
+ * configuration. Defaults to multiple of 10KB. See {@link PKCS7_10KB}.
+ * @returns {Uint8Array} The unpadded input.
+ */
+export function unpad(
+ data: Uint8Array,
+ { blockSizeBytes }: PaddingConfiguration = PKCS7_10KB,
+): Uint8Array {
+ const blockUnpadded = superblockUnpad(data, blockSizeBytes);
+ return pkcs7unpad(blockUnpadded);
+}
+
/**
* PKCS#7 padding function for `Uint8Array` data.
*
@@ -23,7 +73,7 @@
* @param {number} blockSizeBytes - The block size in bytes.
* @returns {Uint8Array} The padded data as a new Uint8Array.
*/
-export function pkcs7pad(data: Uint8Array, blockSizeBytes: number): Uint8Array {
+function pkcs7pad(data: Uint8Array, blockSizeBytes: number): Uint8Array {
invariant(blockSizeBytes > 0, 'block size must be positive');
invariant(blockSizeBytes < 256, 'block size must be less than 256');
const padding = blockSizeBytes - (data.length % blockSizeBytes);
@@ -40,7 +90,7 @@
* @returns {Uint8Array} The unpadded data as a new Uint8Array.
* @throws {Error} If the padding is invalid.
*/
-export function pkcs7unpad(data: Uint8Array): Uint8Array {
+function pkcs7unpad(data: Uint8Array): Uint8Array {
const padding = data[data.length - 1];
invariant(padding > 0, 'padding must be positive');
invariant(data.length >= padding, 'data length must be at least padding');
@@ -63,7 +113,7 @@
* @param {number} superblockSize - The superblock size in blocks.
* @returns {Uint8Array} The block-padded data as a new Uint8Array.
*/
-export function superblockPad(
+function superblockPad(
data: Uint8Array,
blockSizeBytes: number,
superblockSize: number,
@@ -96,10 +146,7 @@
* @param {number} blockSizeBytes - The block size in bytes.
* @returns {Uint8Array} - The unpadded data as a new Uint8Array.
*/
-export function superblockUnpad(
- data: Uint8Array,
- blockSizeBytes: number,
-): Uint8Array {
+function superblockUnpad(data: Uint8Array, blockSizeBytes: number): Uint8Array {
invariant(blockSizeBytes > 0, 'block size must be positive');
invariant(blockSizeBytes <= 255, 'block size must be less than 256');
invariant(
@@ -121,3 +168,11 @@
const unpaddedData = data.subarray(0, unpaddedLengthBytes);
return unpaddedData;
}
+
+// exported for testing purposes only
+export const testing = {
+ pkcs7pad,
+ pkcs7unpad,
+ superblockPad,
+ superblockUnpad,
+};
diff --git a/lib/utils/pkcs7-padding.test.js b/lib/utils/pkcs7-padding.test.js
--- a/lib/utils/pkcs7-padding.test.js
+++ b/lib/utils/pkcs7-padding.test.js
@@ -1,11 +1,7 @@
// @flow
-import {
- pkcs7pad,
- pkcs7unpad,
- superblockPad,
- superblockUnpad,
-} from './pkcs7-padding.js';
+import type { PaddingConfiguration } from './pkcs7-padding';
+import { pad, unpad, testing } from './pkcs7-padding.js';
describe('PKCS#7 Padding', () => {
it('should pad data to a multiple of blockSize bytes', () => {
@@ -13,7 +9,7 @@
const data = generateRandomData(100);
const expectedPadding = 16 - (data.length % blockSize);
- const padded = pkcs7pad(data, blockSize);
+ const padded = testing.pkcs7pad(data, blockSize);
expect(padded.length % 16).toBe(0);
expect(padded[padded.length - 1]).toBe(expectedPadding);
});
@@ -23,15 +19,15 @@
const data = generateRandomData(16);
const expectedPadding = 16;
- const padded = pkcs7pad(data, blockSize);
+ const padded = testing.pkcs7pad(data, blockSize);
expect(padded.length % 16).toBe(0);
expect(padded[padded.length - 1]).toBe(expectedPadding);
});
it('pkcs7pad should fail if blockSize is out of 1-255 range', () => {
const data = generateRandomData(16);
- expect(() => pkcs7pad(data, 0)).toThrow();
- expect(() => pkcs7pad(data, 256)).toThrow();
+ expect(() => testing.pkcs7pad(data, 0)).toThrow();
+ expect(() => testing.pkcs7pad(data, 256)).toThrow();
});
it('pkcs7unpad should unpad data', () => {
@@ -41,7 +37,7 @@
...[6, 6, 6, 6, 6, 6],
]);
- const unpadded = pkcs7unpad(padded);
+ const unpadded = testing.pkcs7unpad(padded);
expect(unpadded.length).toBe(10);
});
@@ -51,7 +47,7 @@
...generateRandomArray(10),
...[0, 0, 0, 0, 0, 0],
]);
- expect(() => pkcs7unpad(padded)).toThrow();
+ expect(() => testing.pkcs7unpad(padded)).toThrow();
});
it('pkcs7unpad should throw if the padding length is > blockSize', () => {
@@ -60,7 +56,7 @@
...generateRandomArray(10),
...[17, 17, 17, 17, 17, 17],
]);
- expect(() => pkcs7unpad(padded)).toThrow();
+ expect(() => testing.pkcs7unpad(padded)).toThrow();
});
it('pkcs7unpad should throw if the padding is invalid', () => {
@@ -69,14 +65,14 @@
...generateRandomArray(10),
...[6, 1, 2, 3, 4, 6],
]);
- expect(() => pkcs7unpad(padded)).toThrow();
+ expect(() => testing.pkcs7unpad(padded)).toThrow();
});
it('pkcs7pad and pkcs7unpad should be inverses', () => {
const blockSize = 16;
const data = generateRandomData(100);
- const padded = pkcs7pad(data, blockSize);
- const unpadded = pkcs7unpad(padded);
+ const padded = testing.pkcs7pad(data, blockSize);
+ const unpadded = testing.pkcs7unpad(padded);
expect(unpadded.length).toBe(data.length);
expect(unpadded).toEqual(data);
});
@@ -91,7 +87,11 @@
const expectedBlockPadding = 1;
const data = generateRandomData(dataLengthBytes);
- const padded = superblockPad(data, blockSizeBytes, superblockSizeBlocks);
+ const padded = testing.superblockPad(
+ data,
+ blockSizeBytes,
+ superblockSizeBlocks,
+ );
expect(padded.length % expectedPaddedLength).toBe(0);
expect(padded[padded.length - 1]).toBe(expectedBlockPadding);
@@ -105,7 +105,11 @@
const expectedBlockPadding = 4;
const data = generateRandomData(dataLengthBytes);
- const padded = superblockPad(data, blockSizeBytes, superblockSizeBlocks);
+ const padded = testing.superblockPad(
+ data,
+ blockSizeBytes,
+ superblockSizeBlocks,
+ );
expect(padded.length % expectedPaddedLength).toBe(0);
expect(padded[padded.length - 1]).toBe(expectedBlockPadding);
@@ -120,7 +124,7 @@
...new Array(2 * 16).fill(2),
]);
- const unpadded = superblockUnpad(padded, blockSizeBytes);
+ const unpadded = testing.superblockUnpad(padded, blockSizeBytes);
expect(unpadded.length).toBe(32);
expect(unpadded).toEqual(padded.subarray(0, 32));
@@ -132,7 +136,7 @@
...generateRandomArray(2 * 16),
...new Array(2 * 16).fill(0),
]);
- expect(() => superblockUnpad(padded, blockSizeBytes)).toThrow();
+ expect(() => testing.superblockUnpad(padded, blockSizeBytes)).toThrow();
});
it('superblockUnpad should throw if the padding length is > num blocks', () => {
@@ -142,7 +146,7 @@
...generateRandomArray(2 * 16),
...new Array(2 * 16).fill(5),
]);
- expect(() => superblockUnpad(padded, blockSizeBytes)).toThrow();
+ expect(() => testing.superblockUnpad(padded, blockSizeBytes)).toThrow();
});
it('superblockUnpad should throw if the padding is invalid', () => {
@@ -152,7 +156,67 @@
...generateRandomArray(2 * 15),
...[1],
]);
- expect(() => superblockUnpad(padded, blockSizeBytes)).toThrow();
+ expect(() => testing.superblockUnpad(padded, blockSizeBytes)).toThrow();
+ });
+});
+
+describe('padding integration tests (pad and unpad)', () => {
+ it('should pad data to a multiple of superblockSize blocks', () => {
+ const config: PaddingConfiguration = {
+ blockSizeBytes: 16,
+ superblockSizeBlocks: 4,
+ };
+
+ // 20 bytes of data - expected 4 blocks total (1 superblock):
+ // - block 1 (16 bytes) = 16 bytes of data
+ // - block 2 (16 bytes) = 4 bytes of data (remaining 12 bytes of padding)
+ // - block 3 (16 bytes) = full padding (filled with 2s)
+ // - block 4 (16 bytes) = full padding (filled with 2s)
+ const dataLengthBytes = 20;
+ const expectedPkcs7Padding = 12;
+ const expectedBlockPadding = 2;
+ const expectedPaddedLength = 4 * 16;
+
+ const data = generateRandomData(dataLengthBytes);
+ const padded = pad(data, config);
+
+ expect(padded.length % expectedPaddedLength).toBe(0);
+ expect(padded[padded.length - 1]).toBe(expectedBlockPadding);
+ expect(padded[2 * 16 - 1]).toBe(expectedPkcs7Padding);
+ });
+
+ it('pad should add a full superblock if pkcs7-padded input is a multiple of superblockSize blocks', () => {
+ const config: PaddingConfiguration = {
+ blockSizeBytes: 16,
+ superblockSizeBlocks: 4,
+ };
+
+ // 5 bytes less so pkcs7 padding is 5 bytes and pads equally to superblock
+ // size
+ const pkcs7paddingLength = 5;
+ const dataLengthBytes = 4 * 16 - pkcs7paddingLength;
+
+ const expectedPaddedLength = 8 * 16;
+ const expectedBlockPadding = 4;
+
+ const data = generateRandomData(dataLengthBytes);
+ const padded = pad(data, config);
+
+ expect(padded.length % expectedPaddedLength).toBe(0);
+ expect(padded[padded.length - 1]).toBe(expectedBlockPadding);
+ });
+
+ it('pad and unpad should be inverses', () => {
+ const config: PaddingConfiguration = {
+ blockSizeBytes: 16,
+ superblockSizeBlocks: 4,
+ };
+
+ const data = generateRandomData(100);
+ const padded = pad(data, config);
+ const unpadded = unpad(padded, config);
+ expect(unpadded.length).toBe(data.length);
+ expect(unpadded).toEqual(data);
});
});

File Metadata

Mime Type
text/plain
Expires
Thu, Dec 5, 1:32 AM (13 h, 51 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2615489
Default Alt Text
D7061.id23693.diff (10 KB)

Event Timeline