Skip to content
This repository has been archived by the owner on Apr 17, 2024. It is now read-only.

Commit

Permalink
Introduce a helper function readNBytes() to KeyFactory class and …
Browse files Browse the repository at this point in the history
…use it instead of `InputStream.read()` for key derivation. This is to help avoid situations where the necessary random bytes were provided by the InputStream but could not be fully read by the `read()` method. We cannot use `InputStream.readNBytes()` since it's only available since JDK 9, and we must be JDK 8-compatible for our Android targets.

PiperOrigin-RevId: 501799128
  • Loading branch information
LizaTretyakova authored and Copybara-Service committed Jan 13, 2023
1 parent 72208aa commit b3d5b0c
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.MessageLite;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.util.Collections;
Expand Down Expand Up @@ -241,6 +242,29 @@ public KeyProtoT deriveKey(KeyFormatProtoT keyFormat, InputStream pseudoRandomne
public Map<String, KeyFormat<KeyFormatProtoT>> keyFormats() throws GeneralSecurityException {
return Collections.emptyMap();
}

/**
* Reads {@code output.length} number of bytes of (pseudo)randomness from the {@code input}
* stream into the provided {@code output} buffer.
*
* Note that this method will not close the {@code input} stream.
*
* @throws GeneralSecurityException when not enough randomness was provided in the {@code input}
* stream.
*/
protected static void readFully(InputStream input, byte[] output)
throws IOException, GeneralSecurityException {
int len = output.length;
int read;
int readTotal = 0;
while (readTotal < len) {
read = input.read(output, readTotal, len - readTotal);
if (read == -1) {
throw new GeneralSecurityException("Not enough pseudorandomness provided");
}
readTotal += read;
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import com.google.protobuf.ByteString;
import com.google.protobuf.ExtensionRegistryLite;
import com.google.protobuf.InvalidProtocolBufferException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
Expand Down Expand Up @@ -184,6 +186,58 @@ public Primitive1 getPrimitive(AesGcmKey key) {
}));
}

@Test
public void readNBytes_works() throws Exception {
byte randomness = 4;
InputStream fragmentedInputStream =
new InputStream() {
@Override
public int read() {
return 0;
}

@Override
public int read(byte[] b, int off, int len) {
b[off] = randomness;
return 1;
}
};
byte[] readBytes = new byte[4];

KeyTypeManager.KeyFactory.readFully(fragmentedInputStream, readBytes);

assertThat(readBytes).isEqualTo(new byte[]{4, 4, 4, 4});
}

@Test
public void readNBytes_throwsOnNotEnoughPseudorandomness() throws Exception {
byte randomness = 4;
InputStream shortInputStream =
new InputStream() {
int numReads = 3;

@Override
public int read() {
return 0;
}

@Override
public int read(byte[] b, int off, int len) {
if (numReads == 0) {
return -1;
}
--numReads;
b[off] = randomness;
return 1;
}
};
byte[] readBytes = new byte[4];

assertThrows(
GeneralSecurityException.class,
() -> KeyTypeManager.KeyFactory.readFully(shortInputStream, readBytes));
}

private static final class Primitive1 {
public Primitive1(ByteString keyValue) {
this.keyValue = keyValue;
Expand Down

0 comments on commit b3d5b0c

Please sign in to comment.