Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3394218
D7060.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
6 KB
Referenced Files
None
Subscribers
None
D7060.diff
View Options
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
@@ -1,5 +1,19 @@
// @flow
+/**
+ * This file contains functions for PKCS#7 padding and unpadding, as well as
+ * functions for padding on the block basis. This is used to let the padding
+ * to be larger than maximum PKCS#7 padding block size (255 bytes).
+ *
+ * The main idea of this concept is to pad the input using the standard
+ * PKCS#7 padding, and then pad the result to the neares multiple of
+ * superblocks (blocks of blocks). The procedure is analogous
+ * to the standard PKCS#7 padding, but operates on the block basis instead of
+ * the byte basis. The fill value of the padding is the number of blocks added.
+ *
+ * The PKCS#7 padding is described in RFC 5652, section 6.3.
+ */
+
import invariant from 'invariant';
/**
@@ -39,4 +53,70 @@
return unpadded;
}
-export { pkcs7pad, pkcs7unpad };
+/**
+ * Pads the PKCS#7-padded input on the block basis. Pads the input to have
+ * a length that is a multiple of the superblock size. The input must already
+ * be PKCS#7 padded to the block size.
+ *
+ * @param {Uint8Array} data - The PKCS#7 padded input to be block-padded.
+ * @param {number} blockSizeBytes - The block size in bytes.
+ * @param {number} superblockSize - The superblock size in blocks.
+ * @returns {Uint8Array} The block-padded data as a new Uint8Array.
+ */
+function superblockPad(
+ data: Uint8Array,
+ blockSizeBytes: number,
+ superblockSize: number,
+): Uint8Array {
+ invariant(
+ data.length % blockSizeBytes === 0,
+ 'data length must be a multiple of block size',
+ );
+ invariant(superblockSize > 0, 'superblock size must be positive');
+ invariant(superblockSize <= 255, 'superblock size must be less than 256');
+ invariant(blockSizeBytes > 0, 'block size must be positive');
+ invariant(blockSizeBytes <= 255, 'block size must be less than 256');
+
+ const numBlocks = data.length / blockSizeBytes;
+ const paddingBlocks = superblockSize - (numBlocks % superblockSize);
+ const paddingValue = paddingBlocks;
+
+ const outputBuffer = new Uint8Array(
+ data.length + paddingBlocks * blockSizeBytes,
+ );
+ outputBuffer.set(data);
+ outputBuffer.fill(paddingValue, data.length);
+ return outputBuffer;
+}
+
+/**
+ * Unpads the block-padded input on the block basis.
+ *
+ * @param {Uint8Array} data - The block-padded input to be unpaded.
+ * @param {number} blockSizeBytes - The block size in bytes.
+ * @returns {Uint8Array} - The unpadded data as a new 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(
+ data.length % blockSizeBytes === 0,
+ 'data length must be a multiple of block size',
+ );
+
+ const numBlocks = data.length / blockSizeBytes;
+ const paddingBlocks = data[data.length - 1];
+ invariant(paddingBlocks > 0 && paddingBlocks < numBlocks, 'invalid padding');
+
+ const unpaddedBlocks = numBlocks - paddingBlocks;
+ const unpaddedLengthBytes = unpaddedBlocks * blockSizeBytes;
+ invariant(
+ data.subarray(unpaddedLengthBytes).every(x => x === paddingBlocks),
+ 'invalid padding',
+ );
+
+ const unpaddedData = data.subarray(0, unpaddedLengthBytes);
+ return unpaddedData;
+}
+
+export { 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,6 +1,11 @@
// @flow
-import { pkcs7pad, pkcs7unpad } from './pkcs7-padding.js';
+import {
+ pkcs7pad,
+ pkcs7unpad,
+ superblockPad,
+ superblockUnpad,
+} from './pkcs7-padding.js';
describe('PKCS#7 Padding', () => {
it('should pad data to a multiple of blockSize bytes', () => {
@@ -76,6 +81,80 @@
});
});
+describe('superblock padding', () => {
+ it('should pad data to a multiple of superblockSize blocks', () => {
+ const blockSizeBytes = 16;
+ const superblockSizeBlocks = 4;
+ const dataLengthBytes = 3 * 16;
+ const expectedPaddedLength = 4 * 16;
+ const expectedBlockPadding = 1;
+
+ const data = generateRandomData(dataLengthBytes);
+ const padded = superblockPad(data, blockSizeBytes, superblockSizeBlocks);
+
+ expect(padded.length % expectedPaddedLength).toBe(0);
+ expect(padded[padded.length - 1]).toBe(expectedBlockPadding);
+ });
+
+ it('pad should add a full superblock if input is a multiple of superblockSize blocks', () => {
+ const blockSizeBytes = 16;
+ const superblockSizeBlocks = 4;
+ const dataLengthBytes = 4 * 16;
+ const expectedPaddedLength = 8 * 16;
+ const expectedBlockPadding = 4;
+
+ const data = generateRandomData(dataLengthBytes);
+ const padded = superblockPad(data, blockSizeBytes, superblockSizeBlocks);
+
+ expect(padded.length % expectedPaddedLength).toBe(0);
+ expect(padded[padded.length - 1]).toBe(expectedBlockPadding);
+ });
+
+ it('superblockUnpad should unpad data', () => {
+ const blockSizeBytes = 16;
+
+ // 2 blocks of data + 2 blocks of padding = 4 blocks total (1 superblock)
+ const padded = new Uint8Array([
+ ...generateRandomArray(2 * 16),
+ ...new Array(2 * 16).fill(2),
+ ]);
+
+ const unpadded = superblockUnpad(padded, blockSizeBytes);
+
+ expect(unpadded.length).toBe(32);
+ expect(unpadded).toEqual(padded.subarray(0, 32));
+ });
+
+ it('superblockUnpad should throw if the padding length is 0', () => {
+ const blockSizeBytes = 16;
+ const padded = new Uint8Array([
+ ...generateRandomArray(2 * 16),
+ ...new Array(2 * 16).fill(0),
+ ]);
+ expect(() => superblockUnpad(padded, blockSizeBytes)).toThrow();
+ });
+
+ it('superblockUnpad should throw if the padding length is > num blocks', () => {
+ const blockSizeBytes = 16;
+ // 4 blocks total, but filled with 5s
+ const padded = new Uint8Array([
+ ...generateRandomArray(2 * 16),
+ ...new Array(2 * 16).fill(5),
+ ]);
+ expect(() => superblockUnpad(padded, blockSizeBytes)).toThrow();
+ });
+
+ it('superblockUnpad should throw if the padding is invalid', () => {
+ const blockSizeBytes = 16;
+ const padded = new Uint8Array([
+ ...generateRandomArray(2 * 16),
+ ...generateRandomArray(2 * 15),
+ ...[1],
+ ]);
+ expect(() => superblockUnpad(padded, blockSizeBytes)).toThrow();
+ });
+});
+
function generateRandomData(length: number): Uint8Array {
const data = new Uint8Array(length);
for (let i = 0; i < length; i++) {
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Dec 1, 6:20 PM (21 h, 27 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2605781
Default Alt Text
D7060.diff (6 KB)
Attached To
Mode
D7060: [lib] Introduce superblock padding
Attached
Detach File
Event Timeline
Log In to Comment