Page MenuHomePhabricator

D7060.diff
No OneTemporary

D7060.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
@@ -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

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)

Event Timeline