-
Notifications
You must be signed in to change notification settings - Fork 277
jwe using Curve P521, ECDH-ES, and A256GCM does not always work with with other implementations #228
Comments
Can you give us more information to help debug?
|
go version: go version go1.11.8 darwin/amd64 Java Libraries POM:
JWK: Encryption Text: "test string" Sample Encryption from Java (copied into Go Program and attempt decryption): eyJlcGsiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTUyMSIsIngiOiJBUHVxYm5Kd29sSnV4NzRLVjRGSlJDMUZDejVFRzVxQ0pkNlh4TThkVm5DVTlrR2lwaktOVWk2T3BORWx1d0xOVzVHV1ozRktNa18wcWFSX0lTTko3Uk1KIiwieSI6IkFYUW5UWkRUOUNrTEtzYnAwelBoeEZwVC1CQkQwVmh2S3BIZ0o4VTVYdFM1M09VVzRaeEpmbTZZbkplZEFjV2lMRm1LVlJzR3lrYlN4YjhmdTUwbkh3SjcifSwiZW5jIjoiQTI1NkdDTSIsImFsZyI6IkVDREgtRVMifQ..6GaCfQBmGoZqdSdp.yid9Ty4obq77NwY.g7R-OpwzH4ySkPqcuO6y_w Program Output: X: 4659511641062964782021875499623429969441821155897294879230806072110741671346740049237806735165617028886069507735635813077932132536190200253111449908755248912 Encrypted: eyJlcGsiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTUyMSIsIngiOiJBUHVxYm5Kd29sSnV4NzRLVjRGSlJDMUZDejVFRzVxQ0pkNlh4TThkVm5DVTlrR2lwaktOVWk2T3BORWx1d0xOVzVHV1ozRktNa18wcWFSX0lTTko3Uk1KIiwieSI6IkFYUW5UWkRUOUNrTEtzYnAwelBoeEZwVC1CQkQwVmh2S3BIZ0o4VTVYdFM1M09VVzRaeEpmbTZZbkplZEFjV2lMRm1LVlJzR3lrYlN4YjhmdTUwbkh3SjcifSwiZW5jIjoiQTI1NkdDTSIsImFsZyI6IkVDREgtRVMifQ..6GaCfQBmGoZqdSdp.yid9Ty4obq77NwY.g7R-OpwzH4ySkPqcuO6y_w panic: square/go-jose: error in cryptographic primitive goroutine 1 [running]: |
Here is a sample encryption (just keep running the java program encrypting the same text until I find one that is successful decrypting in go) that works: eyJlcGsiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTUyMSIsIngiOiJBTWpjeWNSNW9yWW9sRDZoa1ByM0NGVlVHNm8tRjVreFNpNHJ4dUNGVXNsRGcwUTg1Q2lJWXJtZmRuR1MzblZoR3lvV0RXVEJTakpJTXpoV3ZSWDV0ODZEIiwieSI6IkFYRk82TklsNFY2MnB2OGhGY0kwRk14QWFmbkZ0MDQxSzJSUFlEYks1TG5rZFRtOXgwcGdiWlk5ck9rdEpTdXZUMnk2WGdfS0tNTjhqdk9OaE1ZdDJkV20ifSwiZW5jIjoiQTI1NkdDTSIsImFsZyI6IkVDREgtRVMifQ..VuJ8ahL6lKq1nez7.8wuC1K6mxM_rtzo.HBs5Hd2N1s-03Sg6lKuQhg |
Is that encrypted with (to) the same key as the broken one? |
Yes... this is how the keypair is generated in Java: ( I then printed it out and parsed in Go to a keypair) String x = "AVuFsno89wJ5xT2z63iznxVO8H5gsfcHmS1XJ_JbfEzIsudqjrvKGrzxJT96-dmP_NY7KeMvyJEUInmqcqCWbzcQ"; return new ECKey.Builder(Curve.P_521, new Base64URL(x), new Base64URL(y)) |
Taking a quick look I don't see any obvious differences. Can you try this with P-256 instead of P-521? I have a suspicion that the bug might stem from the fact that the x/y params must be padded, and the 521-bit x/y values from P-521 don't round nicely to a byte boundary. It looks like the x/y values in the header are, indeed, 66 bytes in length, but maybe they were padded incorrectly. |
I have run it 5 times with no errors using P-256. That never happened using P-521. The problem is I have to use P-521 to be compatible with client applications. |
That means there's likely a bug in how either go-jose or nimbus-jose-jwt handles the padding on the 521-bit ephemeral key embedded in the header. Here's the X and Y of the EPK on the working ciphertext:
And here's the broken one:
Unfortunately I don't see an obvious difference here. I don't have a Java env set up right now, could you instrument nimus-jose-jwt such that it dumps the X/Y values for the ephemeral public key used for ECDH and then compare those values to what it outputs in the final message? |
Got your message. I will but it will be a little while. Got pulled in another direction. |
Hi, As commented in this SO question, the EPK looks to be on the curve. My assumption is that the agreed key is wrongly computed. Can you compute the key agreement for the following keys? Key 1 => You should have |
I can try that, but the computation path for P-256 and P-521 is the same. Given that it works with one curve but not the other I'm skeptical that the issue is in there. Right now I believe it is an issue with how the X/Y values are parsed or serialized on one or the other side. For the JWKs posted above, what are the X/Y values in decimal that the Java library had parsed (generated) at runtime? |
@Spomky What are the alg-id, apu/apv, values you used for that key derivation? |
I’m not convinced the issue comes from the serialization of the point.
The alg ID corresponds to the content encryption algorithm i.e. The PHP implementation I use may help you. With the A256GCM algorithms, the values used to derive the CEK are:
Result should be |
Tried to reproduce that, but wasn't able to. The keys you posted seem to have X/Y values that are of the incorrect length:
All of these need to be correctly padded to 66 bytes for P-521, as 521/8 = 65.125, which yields 66 bytes rounded up. See here: https://tools.ietf.org/html/rfc7518#section-6.2.1.2 |
Indeed that’s correct. Here are the correct keys:
This does not change the key agreement and the computed derived key values. |
Thanks @Spomky. I'm getting the same results for ECDH as you are:
Here's the test code I ran:
|
So, this bug is still a bit of a mystery. @dodgeware I'd be curious to know what the internal X/Y values are in the Java version and what algorithm id is passed to the derivation. Maybe there's a disagreement there somewhere. |
I am not available to do this right now. As soon as I get time to come back to this I will. |
I managed to reproduce this with jose4j, here's what I found. First, the code to repro:
Here's the X/Y/D values (match in Go/Java):
Shared secret from scalar mult in Go:
Shared secret from scalar mult in Java:
Since this works fine for P-256/P-384, and I don't see any diff in the inputs in the code, the only other reason this isn't working that I can think of is that either Java or Go has a broken P-521 implementation? Seems unlikely. I'll see if I can repro this with another Golang JOSE implementation. |
Looks like the two other popular JOSE libraries I was able to find (lestrrat-go/jwx and dgrijalva/jwt-go) don't seem to support JWE+ECDH-ES, if someone knows of another JOSE library written in Golang please let me know. |
Alright, I figured it out. It was cutting off the first byte of the Z value returned from the scalar base mult giving us a 65-byte array as opposed to a 66-byte one because calling z.Bytes() on a big integer strips the leading zero. This caused input to the the KDF to be incorrect as a result. I'll open a pull request in a sec. |
Fixes issue #228. After calling ScalarMult for P-521, the output can sometimes be 65 bytes long instead of 66 bytes. This happens when the first bit of the computed value is zero. Calling z.Bytes() on the big integer will then omit the leading zero, giving us a 65-byte value. This subsequently causes the shared secret computation to be incorrect as the input into the KDF function should always be 66 bytes which is the full length for a P-521 coordinate value.
That is great. I appreciate the effort. I have not been able to get back to this so this news is great. |
Could we get a release cut for this bug? |
Fixes issue #228. After calling ScalarMult for P-521, the output can sometimes be 65 bytes long instead of 66 bytes. This happens when the first bit of the computed value is zero. Calling z.Bytes() on the big integer will then omit the leading zero, giving us a 65-byte value. This subsequently causes the shared secret computation to be incorrect as the input into the KDF function should always be 66 bytes which is the full length for a P-521 coordinate value.
I am not able to encrypt/decrypt within Java and Go using Curve P521, ECDH-ES, and A256GCM using the same values for curve X, Y, and D. When I try to take the encrypted value from Java and Decrypt in Go it fails (about half the time) and vice versa it fails with:
Go: square/go-jose: error in cryptographic primitive
Sometimes it works though.
I am manually creating the KeyPair in both programs with the same X, Y, and D. Here is a link to my stackoverflow question.
https://stackoverflow.com/questions/55711277/why-do-i-get-an-error-decrypting-jwe-between-java-and-go
The text was updated successfully, but these errors were encountered: