Changeset View
Changeset View
Standalone View
Standalone View
lib/utils/pkcs7-padding.js
// @flow | // @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'; | import invariant from 'invariant'; | ||||
/** | /** | ||||
* PKCS#7 padding function for `Uint8Array` data. | * PKCS#7 padding function for `Uint8Array` data. | ||||
* | * | ||||
* @param {Uint8Array} data - The data to be padded. | * @param {Uint8Array} data - The data to be padded. | ||||
* @param {number} blockSizeBytes - The block size in bytes. | * @param {number} blockSizeBytes - The block size in bytes. | ||||
* @returns {Uint8Array} The padded data as a new Uint8Array. | * @returns {Uint8Array} The padded data as a new Uint8Array. | ||||
Show All 23 Lines | invariant( | ||||
data.subarray(data.length - padding).every(x => x === padding), | data.subarray(data.length - padding).every(x => x === padding), | ||||
'invalid padding', | 'invalid padding', | ||||
); | ); | ||||
const unpadded = data.subarray(0, data.length - padding); | const unpadded = data.subarray(0, data.length - padding); | ||||
return unpadded; | 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 }; |