From 0cff1e0444a64377043c93a68e9966d866004a6a Mon Sep 17 00:00:00 2001 From: Daniel DeGroff Date: Thu, 29 Feb 2024 10:14:22 -0700 Subject: [PATCH] Add additional copy to explain the padding that we may encounter when extracting the r and s components, and how the result must be padded to correctly fill the result. --- .../io/fusionauth/jwt/ec/ECDSASignature.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/java/io/fusionauth/jwt/ec/ECDSASignature.java b/src/main/java/io/fusionauth/jwt/ec/ECDSASignature.java index 1b31f9c..6cdd548 100644 --- a/src/main/java/io/fusionauth/jwt/ec/ECDSASignature.java +++ b/src/main/java/io/fusionauth/jwt/ec/ECDSASignature.java @@ -70,6 +70,7 @@ public byte[] derDecode(Algorithm algorithm) throws IOException { byte[] r = sequence[0].getPositiveBigInteger().toByteArray(); byte[] s = sequence[1].getPositiveBigInteger().toByteArray(); + // The length of the result is fixed and discrete per algorithm. byte[] result; switch (algorithm) { case ES256: @@ -85,8 +86,26 @@ public byte[] derDecode(Algorithm algorithm) throws IOException { throw new IllegalArgumentException("Unable to decode the signature for algorithm [" + algorithm.name() + "]"); } + // Because the response is not encoded, the r and s component must take up an equal amount of the resulting array. + // This allows the consumer of this value to always safely split the value in half based upon an index value since + // the result is not encoded and does not contain any meta-data about the contents. int len = result.length / 2; + // The extracted byte array of the DER encoded value can be left padded. For this reason, the component lengths + // may be greater than 'len' which is half of the result. So for example, if r is left padded, the length may be + // equal to 67 in ES512 even though len is only 66. This is why we must calculate the source position for reading + // when we copy the r byte array into the result. The same is potentially true for either component. We cannot + // make an assumption that the source position in r or s will be 0. + // + // Similarly, when the r and s components are not padded, but they are shorter than len, we need to pad the value + // to be right aligned in the result. This is why the destination position may not be 0 or len respectively for + // r and s. + // + // If s is 65 bytes, then the destination position in the 0 initialized resulting array needs to be len + 1 so that + // we write the final byte of s at the end of the result. + // + // For clarity, calculate each input to the arraycopy method first. + int rSrcPos = r.length > len ? (r.length - len) : 0; int rDstPos = Math.max(0, len - r.length); int rLength = Math.min(r.length, len);