Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decrypting a compressed JWT from ruby-jwe fails #37

Closed
hagmonk opened this issue Oct 21, 2016 · 6 comments
Closed

Decrypting a compressed JWT from ruby-jwe fails #37

hagmonk opened this issue Oct 21, 2016 · 6 comments

Comments

@hagmonk
Copy link
Contributor

hagmonk commented Oct 21, 2016

I was attempting to decode a JWT from ruby-jwe that had been zipped.

This library falls through to Zlib::Deflate to handle compression. This creates a problem for buddy.util.deflate/uncompress, which gets an exception:

CompilerException java.util.zip.ZipException: invalid stored block lengths

The root cause is creating the Inflater with nowrap set to true. Setting this value to false results in correct decompression. But I spent more time looking at this than I should have :)

Reading the docs it's kind of confusing because it states:

If the parameter 'nowrap' is true then the ZLIB header and checksum fields will not be used.

This makes it sound like they're harmless to have left in there, but it clearly fails if set to true.

Then it goes on to say

Note: When using the 'nowrap' option it is also necessary to provide an extra "dummy" byte as input. This is required by the ZLIB native library in order to support certain optimizations.

You're using the nowrap option but you're not passing in any dummy bytes (that I saw). Yet this apparently doesn't break unwrapped input.

I spent some time fiddling but can't afford to go too far down the bit twiddling rabbit hole. So instead I have a "just try it again" patch that you might be ok with, or you might want to root cause further. Of course if there's anything I can do to help just let me know. Pull request incoming.

@hagmonk
Copy link
Contributor Author

hagmonk commented Oct 21, 2016

One caveat is that even when using with-redefs-fn to substitute in my own routine, I still can't take the output and continue to the next stage ("decoding" the JWT which I think is "unsign" in buddy.sign) … so I have to also consider the possibility that my patch might make an exception go away but the root cause is something else.

@hagmonk
Copy link
Contributor Author

hagmonk commented Oct 21, 2016

So I made it work, but interestingly, I had to create an intermediate string to feed into jwt/unsign. That doesn't look like an issue introduced with my patch, since byte arrays appears to be what's passed around even without compression involved.

(with-redefs-fn
  {#'buddy.util.deflate/uncompress
   (fn [^bytes input]
     (let [buf  (byte-array 1024)
           os   (ByteArrayOutputStream.)
           opts (Inflater. false)]
       (with-open [is  (ByteArrayInputStream. input)
                   iis (InflaterInputStream. is opts)]
         (loop []
           (let [readed (.read iis buf)]
             (when (pos? readed)
               (.write os buf 0 readed)
               (recur))))
         (.toByteArray os))))}

  #(let [bridge-data (pjson.core/read-str (slurp "token.tmp"))
         private-key (keys/private-key "jwt-rsa-key.tmp")
         secret  (slurp "jwt-secret.tmp")
         token       (get-in bridge-data ["bridgeAuth" "token"])]
    (-> token
        (buddy.sign.jwe/decrypt
          private-key
          {:alg :rsa-oaep
           :enc :a128gcm
           :zip "DEF"})
        (String.)
        (buddy.sign.jwt/unsign secret {:alg :hs256}))))

@niwinz
Copy link
Member

niwinz commented Oct 21, 2016

Hi @hagmonk

After refreshing a little bit the memory, I think that the buddy impl is correct. The JWE RFC specifies that deflate (RFC1951) should be used. The JDK Deflater/Inflater with nowrap parameter as true produces as expected the defalte (RFC1951) stream of bytes.

Looking at the ruby impl, it seems that it uses the zlib library as is, that by default generates zlib stream of bytes (RFC1950), that is incorrect in terms of JWE specification. So I think that it should be fixed in ruby impl.

It can be fixed just passing the correct windowBits parameter to Inflate and Deflate. More documentation:

@hagmonk
Copy link
Contributor Author

hagmonk commented Oct 21, 2016

Cool, thanks for the research here. I will file an issue with those guys.

What are your thoughts on applying Postel's law in this case? Adhere strictly to JWE RFC for data you generate, but tolerate some non-comformant incoming data as long as the intent is clear?

@niwinz
Copy link
Member

niwinz commented Oct 23, 2016

buddy-core 1.1.0 released with the proposed improvement.

@niwinz niwinz closed this as completed Oct 23, 2016
@hagmonk
Copy link
Contributor Author

hagmonk commented Oct 23, 2016

Thank you @niwinz !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants