You are viewing a plain text version of this content. The canonical link for it is here.
Posted to proton@qpid.apache.org by "Philip Harvey (JIRA)" <ji...@apache.org> on 2012/11/28 21:41:58 UTC

[jira] [Assigned] (PROTON-171) after connection close, proton-c SSL decryption fails when left-over bytes passed to input()

     [ https://issues.apache.org/jira/browse/PROTON-171?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Philip Harvey reassigned PROTON-171:
------------------------------------

    Assignee: Ken Giusti
    
> after connection close, proton-c SSL decryption fails when left-over bytes passed to input()
> --------------------------------------------------------------------------------------------
>
>                 Key: PROTON-171
>                 URL: https://issues.apache.org/jira/browse/PROTON-171
>             Project: Qpid Proton
>          Issue Type: Bug
>          Components: proton-c
>            Reporter: Philip Harvey
>            Assignee: Ken Giusti
>
> We've been working on the Java SSL implementation and are seeing a test fail against proton-c but that works against proton-j.  We believe this is due to a bug in proton-c.
> One of the scenarios we wanted to cover in our testing was the case where the Transport input method leaves "left-overs", e.g. when you call server.input() with 100 bytes of input, but it only accepts 20, as indicated by its return value.  
> For example, we expect this to happen if the preceding client.output() call is told to write to a buffer sized such that its output contains a trailing *fragment* of an SSL packet, which input() won't be able to decipher.
> We therefore modified the pump method in proton/tests/proton_tests/ssl.py to handle this case.  In its loop, it now captures the bytes "left over" after calling input(), and prepends them to the input() invocation in the next iteration.  The buffer size is now a parameter so individual tests can exercise the packet fragmenting behaviour described above.
> We made the following change:
> -------
> diff --git a/tests/proton_tests/ssl.py b/tests/proton_tests/ssl.py
> index 8567b1b..237c3da 100644
> --- a/tests/proton_tests/ssl.py
> +++ b/tests/proton_tests/ssl.py
> @@ -43,13 +43,32 @@ class SslTest(common.Test):
>          self.t_client = None
>          self.t_server = None
>  
> -    def _pump(self):
> +    def _pump(self, buffer_size=1024):
> +        """
> +        Make the transport send up to buffer_size bytes (this will be the AMQP
> +        header and open frame) returning a buffer containing the bytes
> +        sent.  Transport is stateful so this will return 0 when it has
> +        no more frames to send.
> +        TODO this function is duplicated in sasl.py. Should be moved to a common place.
> +        """
> +        out_client_leftover_by_server = ""
> +        out_server_leftover_by_client = ""
> +        i=0
>          while True:
> -            out_client = self.t_client.output(1024)
> -            out_server = self.t_server.output(1024)
> -            if out_client: self.t_server.input(out_client)
> -            if out_server: self.t_client.input(out_server)
> +
> +            out_client = out_client_leftover_by_server + self.t_client.output(buffer_size)
> +            out_server = out_server_leftover_by_client + self.t_server.output(buffer_size)
> +
> +            if out_client:
> +                number_server_consumed = self.t_server.input(out_client)
> +                out_client_leftover_by_server = out_client[number_server_consumed:] # if it consumed everything then this is empty
> +
> +            if out_server:
> +                number_client_consumed = self.t_client.input(out_server)
> +                out_server_leftover_by_client = out_server[number_client_consumed:] # if it consumed everything then this is empty
> +
>              if not out_client and not out_server: break
> +            i=i+1
>  
>      def _testpath(self, file):
>          """ Set the full path to the certificate,keyfile, etc. for the test.
> -------
> Several ssl tests now fail when run against proton-c, all with the same error.  This surprised us because we hadn't started playing with the buffer size yet - we were still using the default of 1024.
> For example, test_server_authentication gives this output:
> -------
> proton_tests.ssl.SslTest.test_server_authentication .........................................................................[0xa2ca208:0] ERROR[-2] SSL Failure: error:1408F119:SSL routines:SSL3_GET_RECORD:decryption failed or bad record mac
>  fail
> Error during test:  Traceback (most recent call last):
>     File "./tests/proton-test", line 331, in run
>       phase()
>     File "/home/phil/dev/proton/tests/proton_tests/ssl.py", line 166, in test_server_authentication
>       self._pump()
>     File "/home/phil/dev/proton/tests/proton_tests/ssl.py", line 63, in _pump
>       number_server_consumed = self.t_server.input(out_client)
>     File "/home/phil/dev/proton/proton-c/bindings/python/proton.py", line 2141, in input
>       return self._check(n)
>     File "/home/phil/dev/proton/proton-c/bindings/python/proton.py", line 2115, in _check
>       raise exc("[%s]: %s" % (err, pn_error_text(pn_transport_error(self._trans))))
>   TransportException: [-2]: SSL Failure: error:1408F119:SSL routines:SSL3_GET_RECORD:decryption failed or bad record mac
> Totals: 1 tests, 0 passed, 0 skipped, 0 ignored, 1 failed
> -------
> The first pump() call in this test works fine; the failure we see is when it's invoked again after closing the connection.
> The problem is that the previous t_server.input call didn't accept *any* of the bytes given to it.  On the next t_server.input call, these bytes are prepended to the newly-produced ones from t_client.output, which seems reasonable to us, but this produces the error above.

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators
For more information on JIRA, see: http://www.atlassian.com/software/jira