Skip to content
Merged
6 changes: 0 additions & 6 deletions sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,6 @@ public byte getECCModeAsByte() {
return (byte) value;
}

public static int getECDSASignatureStructSize(NanoTDFType.ECCurve curve) {
int keySize = curve.getKeySize();
return (1 + keySize + 1 + keySize);
}


@Nonnull
public NanoTDFType.ECCurve getCurve() {
return NanoTDFType.ECCurve.fromCurveMode(data.curveMode);
Expand Down
41 changes: 33 additions & 8 deletions sdk/src/main/java/io/opentdf/platform/sdk/PolicyInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import java.nio.ByteBuffer;

public class PolicyInfo {
private static final int DEFAULT_BINDING_SIZE = 8;
private NanoTDFType.PolicyType type;
private boolean hasECDSABinding;
private byte[] body;
private byte[] binding;

Expand All @@ -13,7 +13,6 @@ public PolicyInfo() {

public PolicyInfo(ByteBuffer buffer, ECCMode eccMode) {
this.type = NanoTDFType.PolicyType.values()[buffer.get()];
this.hasECDSABinding = eccMode.isECDSABindingEnabled();

if (this.type == NanoTDFType.PolicyType.REMOTE_POLICY) {

Expand Down Expand Up @@ -45,13 +44,40 @@ public PolicyInfo(ByteBuffer buffer, ECCMode eccMode) {
}
}

int bindingBytesSize = 8; // GMAC length
if (this.hasECDSABinding) { // ECDSA - The size of binding depends on the curve.
bindingBytesSize = ECCMode.getECDSASignatureStructSize(eccMode.getCurve());
this.binding = readBinding(buffer, eccMode);
}

static byte[] readBinding(ByteBuffer buffer, ECCMode eccMode) {
byte[] binding;
if (eccMode.isECDSABindingEnabled()) { // ECDSA - The size of binding depends on the curve.
int rSize = getSize(buffer.get(), eccMode.getCurve());
byte[] rBytes = new byte[rSize];
buffer.get(rBytes);
int sSize = getSize(buffer.get(), eccMode.getCurve());
byte[] sBytes = new byte[sSize];
buffer.get(sBytes);
binding = ByteBuffer.allocate(rSize + sSize + 2)
.put((byte) rSize)
.put(rBytes)
.put((byte) sSize)
.put(sBytes)
.array();
} else {
binding = new byte[DEFAULT_BINDING_SIZE];
buffer.get(binding);
}

this.binding = new byte[bindingBytesSize];
buffer.get(this.binding);
return binding;
}

private static int getSize(byte size, NanoTDFType.ECCurve curve) {
int elementSize = Byte.toUnsignedInt(size);
if (elementSize > curve.getKeySize()) {
throw new SDK.MalformedTDFException(
String.format("Invalid ECDSA binding size. Expected signature components to be at most %d bytes but got (%d) bytes for curve %s.",
curve.getKeySize(), elementSize, curve.getCurveName()));
}
return elementSize;
}

public int getTotalSize() {
Expand All @@ -64,7 +90,6 @@ public int getTotalSize() {
if (type == NanoTDFType.PolicyType.EMBEDDED_POLICY_PLAIN_TEXT ||
type == NanoTDFType.PolicyType.EMBEDDED_POLICY_ENCRYPTED) {

int policySize = body.length;
totalSize = (1 + Short.BYTES + body.length + binding.length);
} else {
throw new RuntimeException("Embedded policy with key access is not supported.");
Expand Down
11 changes: 10 additions & 1 deletion sdk/src/main/java/io/opentdf/platform/sdk/SDK.java
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ public static Manifest readManifest(SeekableByteChannel tdfBytes) throws SDKExce
* to get the {@link Manifest} from a TDF.
* @param manifest The {@link Manifest} containing the policy.
* @return The decoded {@link PolicyObject}.
* @throws SDKException if there is an error during decoding.
* @throws {@link SDKException} if there is an error during decoding.
*/
public static PolicyObject decodePolicyObject(Manifest manifest) throws SDKException {
return Manifest.decodePolicyObject(manifest);
Expand All @@ -176,6 +176,15 @@ public String getPlatformUrl() {
return platformUrl;
}

/**
* Indicates that the TDF is malformed in some way
*/
public static class MalformedTDFException extends SDKException {
public MalformedTDFException(String errorMessage) {
super(errorMessage);
}
}

/**
* {@link SplitKeyException} is thrown when the SDK encounters an error related to
* the inability to reconstruct a split key during decryption.
Expand Down
81 changes: 81 additions & 0 deletions sdk/src/test/java/io/opentdf/platform/sdk/PolicyInfoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Random;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.jupiter.api.Assertions.*;

class PolicyInfoTest {
Expand Down Expand Up @@ -58,4 +65,78 @@ void settingAndGettingPolicyBinding() {
policyInfo.setPolicyBinding(binding);
assertArrayEquals(binding, policyInfo.getPolicyBinding());
}


BigInteger getRandomBigInteger(Random rand, int byteLength) {
return new BigInteger((1+rand.nextInt(byteLength-1))*8, rand);
}

@Test
void testReadingSignatureWithComponentSizes() {
var rand = new Random();
var curve = NanoTDFType.ECCurve.SECP256R1;
for (var i = 0; i < 100; i++) {
var rBytes = getRandomBigInteger(rand, curve.getKeySize()).toByteArray();
var sBytes = getRandomBigInteger(rand, curve.getKeySize()) .toByteArray();
var buffer = ByteBuffer.allocate(rBytes.length + sBytes.length + 2);
buffer.put((byte)rBytes.length);
buffer.put(rBytes);
buffer.put((byte) sBytes.length);
buffer.put(sBytes);

var originalSig = Arrays.copyOf(buffer.array(), buffer.position());

buffer.flip();

ECCMode eccMode = new ECCMode();
eccMode.setECDSABinding(true);
eccMode.setEllipticCurve(curve);

byte[] signature = PolicyInfo.readBinding(buffer, eccMode);
assertThat(signature).containsExactly(originalSig);
// make sure we read all bytes so that reading continues after us in the TDF
assertThat(buffer.position()).isEqualTo(buffer.capacity());
}
}

@Test
void testParsingTooBigSignatureComponents() {
{
var rand = new Random();
var curve = NanoTDFType.ECCurve.SECP256R1;
var rBytes = new BigInteger((curve.getKeySize() + 1) * 8, rand).toByteArray();
var sBytes = getRandomBigInteger(rand, curve.getKeySize()).toByteArray();
var buffer = ByteBuffer.allocate(rBytes.length + sBytes.length + 2);
buffer.put((byte) rBytes.length);
buffer.put(rBytes);
buffer.put((byte) sBytes.length);
buffer.put(sBytes);

buffer.flip();

ECCMode eccMode = new ECCMode();
eccMode.setECDSABinding(true);
eccMode.setEllipticCurve(curve);
assertThrows(SDK.MalformedTDFException.class, () -> PolicyInfo.readBinding(buffer, eccMode));
}

{
var rand = new Random();
var curve = NanoTDFType.ECCurve.SECP256R1;
var rBytes = getRandomBigInteger(rand, curve.getKeySize()).toByteArray();
var sBytes = new BigInteger((curve.getKeySize() + 1) * 8, rand).toByteArray();
var buffer = ByteBuffer.allocate(rBytes.length + sBytes.length + 2);
buffer.put((byte) rBytes.length);
buffer.put(rBytes);
buffer.put((byte) sBytes.length);
buffer.put(sBytes);

buffer.flip();

ECCMode eccMode = new ECCMode();
eccMode.setECDSABinding(true);
eccMode.setEllipticCurve(curve);
assertThrows(SDK.MalformedTDFException.class, () -> PolicyInfo.readBinding(buffer, eccMode));
}
}
}
Loading