-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Padded data should be at least 32 bytes (#39)
- Loading branch information
Showing
3 changed files
with
172 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2023 Mountainminds GmbH & Co. KG | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
* SOFTWARE. | ||
* | ||
* SPDX-License-Identifier: MIT | ||
*******************************************************************************/ | ||
package com.mountainminds.three4j; | ||
|
||
import java.io.ByteArrayInputStream; | ||
import java.io.ByteArrayOutputStream; | ||
import java.io.DataInputStream; | ||
import java.io.DataOutputStream; | ||
import java.io.IOException; | ||
import java.security.SecureRandom; | ||
import java.util.Random; | ||
|
||
/** | ||
* Internal utility for PKCS#7 padding. | ||
*/ | ||
class PaddedBuffer extends DataOutputStream { | ||
|
||
private static ThreadLocal<Random> RANDOM = ThreadLocal.withInitial(SecureRandom::new); | ||
|
||
private static final int MSG_LEN_MIN = 32; | ||
private static final int PAD_LEN_MIN = 1; | ||
private static final int PAD_LEN_MAX = 255; | ||
|
||
private final Random random; | ||
|
||
PaddedBuffer(Random random) { | ||
super(new ByteArrayOutputStream()); | ||
this.random = random; | ||
} | ||
|
||
PaddedBuffer() { | ||
this(RANDOM.get()); | ||
} | ||
|
||
byte[] withPadding() throws IOException { | ||
var buffer = (ByteArrayOutputStream) out; | ||
int panLenMin = Math.max(PAD_LEN_MIN, MSG_LEN_MIN - buffer.size()); | ||
int padding = random.nextInt(PAD_LEN_MAX - panLenMin + 1) + panLenMin; | ||
for (int i = 0; i < padding; i++) { | ||
write(padding); | ||
} | ||
return buffer.toByteArray(); | ||
} | ||
|
||
static DataInputStream removePadding(byte[] buffer) { | ||
int len = 0xff & buffer[buffer.length - 1]; | ||
return new DataInputStream(new ByteArrayInputStream(buffer, 0, buffer.length - len)); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
105 changes: 105 additions & 0 deletions
105
src/test/java/com/mountainminds/three4j/PaddedBufferTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2021 Mountainminds GmbH & Co. KG | ||
* | ||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
* SOFTWARE. | ||
* | ||
* SPDX-License-Identifier: MIT | ||
*******************************************************************************/ | ||
package com.mountainminds.three4j; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertArrayEquals; | ||
|
||
import java.io.ByteArrayOutputStream; | ||
import java.io.IOException; | ||
import java.util.Random; | ||
|
||
import org.junit.jupiter.api.Test; | ||
|
||
public class PaddedBufferTest { | ||
|
||
private final Random minRandom = new Random() { | ||
private static final long serialVersionUID = 1L; | ||
|
||
@Override | ||
public int nextInt(int bound) { | ||
return 0; | ||
} | ||
}; | ||
|
||
private final Random maxRandom = new Random() { | ||
private static final long serialVersionUID = 1L; | ||
|
||
@Override | ||
public int nextInt(int bound) { | ||
return bound - 1; | ||
} | ||
}; | ||
|
||
@Test | ||
void finish_should_expand_message_to_at_least_32_bytes() throws IOException { | ||
try (var buffer = new PaddedBuffer(minRandom)) { | ||
buffer.write(0x12); | ||
buffer.write(0x15); | ||
var msg = buffer.withPadding(); | ||
var expected = bytes(0x12, 0x15).nbytes(30, 30).toByteArray(); | ||
assertArrayEquals(expected, msg); | ||
} | ||
} | ||
|
||
@Test | ||
void finish_should_add_at_least_1_byte() throws IOException { | ||
try (var buffer = new PaddedBuffer(minRandom)) { | ||
buffer.write(bytes().nbytes(64, 42).toByteArray()); | ||
var msg = buffer.withPadding(); | ||
var expected = bytes().nbytes(64, 42).nbytes(1, 1).toByteArray(); | ||
assertArrayEquals(expected, msg); | ||
} | ||
} | ||
|
||
@Test | ||
void finish_should_add_at_most_255_bytes() throws IOException { | ||
try (var buffer = new PaddedBuffer(maxRandom)) { | ||
buffer.write(0x42); | ||
buffer.write(0x43); | ||
var msg = buffer.withPadding(); | ||
var expected = bytes(0x42, 0x43).nbytes(255, 255).toByteArray(); | ||
assertArrayEquals(expected, msg); | ||
} | ||
} | ||
|
||
@Test | ||
void removePadding_should_remove_padding_of_length_1() throws IOException { | ||
var data = PaddedBuffer.removePadding(bytes(1, 2, 3).nbytes(1, 1).toByteArray()); | ||
assertArrayEquals(bytes(1, 2, 3).toByteArray(), data.readAllBytes()); | ||
} | ||
|
||
@Test | ||
void removePadding_should_remove_padding_of_length_255() throws IOException { | ||
var data = PaddedBuffer.removePadding(bytes(1, 2, 3).nbytes(255, 255).toByteArray()); | ||
assertArrayEquals(bytes(1, 2, 3).toByteArray(), data.readAllBytes()); | ||
} | ||
|
||
private static Bytes bytes(int... bytes) { | ||
var out = new Bytes(); | ||
for (int b : bytes) { | ||
out.write(0xff & b); | ||
} | ||
return out; | ||
} | ||
|
||
private static class Bytes extends ByteArrayOutputStream { | ||
Bytes nbytes(int count, int value) { | ||
for (int i = 0; i < count; i++) { | ||
write(0xff & value); | ||
} | ||
return this; | ||
} | ||
} | ||
|
||
} |