Skip to content

Commit 3b1bb69

Browse files
fix(sdk): parse the component sizes (#286)
we serialize the components `r` and `s` of an ECDSA signature as: ``` | length of r in bytes (1 byte) | r | length of s in bytes (1 byte) | s | ``` The change to the spec is implemented [here](opentdf/spec#17) and hasn't been merged yet --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent 6ff003c commit 3b1bb69

File tree

4 files changed

+124
-15
lines changed

4 files changed

+124
-15
lines changed

sdk/src/main/java/io/opentdf/platform/sdk/ECCMode.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,6 @@ public byte getECCModeAsByte() {
5555
return (byte) value;
5656
}
5757

58-
public static int getECDSASignatureStructSize(NanoTDFType.ECCurve curve) {
59-
int keySize = curve.getKeySize();
60-
return (1 + keySize + 1 + keySize);
61-
}
62-
63-
6458
@Nonnull
6559
public NanoTDFType.ECCurve getCurve() {
6660
return NanoTDFType.ECCurve.fromCurveMode(data.curveMode);

sdk/src/main/java/io/opentdf/platform/sdk/PolicyInfo.java

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import java.nio.ByteBuffer;
44

55
public class PolicyInfo {
6+
private static final int DEFAULT_BINDING_SIZE = 8;
67
private NanoTDFType.PolicyType type;
7-
private boolean hasECDSABinding;
88
private byte[] body;
99
private byte[] binding;
1010

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

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

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

@@ -45,13 +44,40 @@ public PolicyInfo(ByteBuffer buffer, ECCMode eccMode) {
4544
}
4645
}
4746

48-
int bindingBytesSize = 8; // GMAC length
49-
if (this.hasECDSABinding) { // ECDSA - The size of binding depends on the curve.
50-
bindingBytesSize = ECCMode.getECDSASignatureStructSize(eccMode.getCurve());
47+
this.binding = readBinding(buffer, eccMode);
48+
}
49+
50+
static byte[] readBinding(ByteBuffer buffer, ECCMode eccMode) {
51+
byte[] binding;
52+
if (eccMode.isECDSABindingEnabled()) { // ECDSA - The size of binding depends on the curve.
53+
int rSize = getSize(buffer.get(), eccMode.getCurve());
54+
byte[] rBytes = new byte[rSize];
55+
buffer.get(rBytes);
56+
int sSize = getSize(buffer.get(), eccMode.getCurve());
57+
byte[] sBytes = new byte[sSize];
58+
buffer.get(sBytes);
59+
binding = ByteBuffer.allocate(rSize + sSize + 2)
60+
.put((byte) rSize)
61+
.put(rBytes)
62+
.put((byte) sSize)
63+
.put(sBytes)
64+
.array();
65+
} else {
66+
binding = new byte[DEFAULT_BINDING_SIZE];
67+
buffer.get(binding);
5168
}
5269

53-
this.binding = new byte[bindingBytesSize];
54-
buffer.get(this.binding);
70+
return binding;
71+
}
72+
73+
private static int getSize(byte size, NanoTDFType.ECCurve curve) {
74+
int elementSize = Byte.toUnsignedInt(size);
75+
if (elementSize > curve.getKeySize()) {
76+
throw new SDK.MalformedTDFException(
77+
String.format("Invalid ECDSA binding size. Expected signature components to be at most %d bytes but got (%d) bytes for curve %s.",
78+
curve.getKeySize(), elementSize, curve.getCurveName()));
79+
}
80+
return elementSize;
5581
}
5682

5783
public int getTotalSize() {
@@ -64,7 +90,6 @@ public int getTotalSize() {
6490
if (type == NanoTDFType.PolicyType.EMBEDDED_POLICY_PLAIN_TEXT ||
6591
type == NanoTDFType.PolicyType.EMBEDDED_POLICY_ENCRYPTED) {
6692

67-
int policySize = body.length;
6893
totalSize = (1 + Short.BYTES + body.length + binding.length);
6994
} else {
7095
throw new RuntimeException("Embedded policy with key access is not supported.");

sdk/src/main/java/io/opentdf/platform/sdk/SDK.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ public static Manifest readManifest(SeekableByteChannel tdfBytes) throws SDKExce
166166
* to get the {@link Manifest} from a TDF.
167167
* @param manifest The {@link Manifest} containing the policy.
168168
* @return The decoded {@link PolicyObject}.
169-
* @throws SDKException if there is an error during decoding.
169+
* @throws {@link SDKException} if there is an error during decoding.
170170
*/
171171
public static PolicyObject decodePolicyObject(Manifest manifest) throws SDKException {
172172
return Manifest.decodePolicyObject(manifest);
@@ -176,6 +176,15 @@ public String getPlatformUrl() {
176176
return platformUrl;
177177
}
178178

179+
/**
180+
* Indicates that the TDF is malformed in some way
181+
*/
182+
public static class MalformedTDFException extends SDKException {
183+
public MalformedTDFException(String errorMessage) {
184+
super(errorMessage);
185+
}
186+
}
187+
179188
/**
180189
* {@link SplitKeyException} is thrown when the SDK encounters an error related to
181190
* the inability to reconstruct a split key during decryption.

sdk/src/test/java/io/opentdf/platform/sdk/PolicyInfoTest.java

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
import org.junit.jupiter.api.BeforeEach;
44
import org.junit.jupiter.api.Test;
5+
6+
import java.math.BigInteger;
7+
import java.nio.ByteBuffer;
8+
import java.util.Arrays;
9+
import java.util.Random;
10+
11+
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
512
import static org.junit.jupiter.api.Assertions.*;
613

714
class PolicyInfoTest {
@@ -58,4 +65,78 @@ void settingAndGettingPolicyBinding() {
5865
policyInfo.setPolicyBinding(binding);
5966
assertArrayEquals(binding, policyInfo.getPolicyBinding());
6067
}
68+
69+
70+
BigInteger getRandomBigInteger(Random rand, int byteLength) {
71+
return new BigInteger((1+rand.nextInt(byteLength-1))*8, rand);
72+
}
73+
74+
@Test
75+
void testReadingSignatureWithComponentSizes() {
76+
var rand = new Random();
77+
var curve = NanoTDFType.ECCurve.SECP256R1;
78+
for (var i = 0; i < 100; i++) {
79+
var rBytes = getRandomBigInteger(rand, curve.getKeySize()).toByteArray();
80+
var sBytes = getRandomBigInteger(rand, curve.getKeySize()) .toByteArray();
81+
var buffer = ByteBuffer.allocate(rBytes.length + sBytes.length + 2);
82+
buffer.put((byte)rBytes.length);
83+
buffer.put(rBytes);
84+
buffer.put((byte) sBytes.length);
85+
buffer.put(sBytes);
86+
87+
var originalSig = Arrays.copyOf(buffer.array(), buffer.position());
88+
89+
buffer.flip();
90+
91+
ECCMode eccMode = new ECCMode();
92+
eccMode.setECDSABinding(true);
93+
eccMode.setEllipticCurve(curve);
94+
95+
byte[] signature = PolicyInfo.readBinding(buffer, eccMode);
96+
assertThat(signature).containsExactly(originalSig);
97+
// make sure we read all bytes so that reading continues after us in the TDF
98+
assertThat(buffer.position()).isEqualTo(buffer.capacity());
99+
}
100+
}
101+
102+
@Test
103+
void testParsingTooBigSignatureComponents() {
104+
{
105+
var rand = new Random();
106+
var curve = NanoTDFType.ECCurve.SECP256R1;
107+
var rBytes = new BigInteger((curve.getKeySize() + 1) * 8, rand).toByteArray();
108+
var sBytes = getRandomBigInteger(rand, curve.getKeySize()).toByteArray();
109+
var buffer = ByteBuffer.allocate(rBytes.length + sBytes.length + 2);
110+
buffer.put((byte) rBytes.length);
111+
buffer.put(rBytes);
112+
buffer.put((byte) sBytes.length);
113+
buffer.put(sBytes);
114+
115+
buffer.flip();
116+
117+
ECCMode eccMode = new ECCMode();
118+
eccMode.setECDSABinding(true);
119+
eccMode.setEllipticCurve(curve);
120+
assertThrows(SDK.MalformedTDFException.class, () -> PolicyInfo.readBinding(buffer, eccMode));
121+
}
122+
123+
{
124+
var rand = new Random();
125+
var curve = NanoTDFType.ECCurve.SECP256R1;
126+
var rBytes = getRandomBigInteger(rand, curve.getKeySize()).toByteArray();
127+
var sBytes = new BigInteger((curve.getKeySize() + 1) * 8, rand).toByteArray();
128+
var buffer = ByteBuffer.allocate(rBytes.length + sBytes.length + 2);
129+
buffer.put((byte) rBytes.length);
130+
buffer.put(rBytes);
131+
buffer.put((byte) sBytes.length);
132+
buffer.put(sBytes);
133+
134+
buffer.flip();
135+
136+
ECCMode eccMode = new ECCMode();
137+
eccMode.setECDSABinding(true);
138+
eccMode.setEllipticCurve(curve);
139+
assertThrows(SDK.MalformedTDFException.class, () -> PolicyInfo.readBinding(buffer, eccMode));
140+
}
141+
}
61142
}

0 commit comments

Comments
 (0)