9

I'd like to know if there is an easy way (maybe a library) to write constant-time programs in Python. In particular, I'd like to be able to specify that an if-else flow must always last the same time whether the if condition is True or False.

For instance:

if condition:
    foo1()
else:
    foo2()
foo3()

Running this program in constant-time would mean that the time that it takes to hit f3() should be the same independently of the result of the evaluation of condition. This would prevent a side-channel that could reveal the execution of f1() or f2() (cf. timing attacks).

synack
  • 1,699
  • 3
  • 24
  • 50
  • Could you elaborate, for example, provide the initial code and the expected result. – bereal Aug 09 '14 at 12:09
  • I don't have anything to offer, but thanks for the interesting question. (+1) – NPE Aug 09 '14 at 12:11
  • @bereal: I elaborated a bit more. It's not a specific piece of code I'm talking about. It's a general question. – synack Aug 09 '14 at 12:13
  • 7
    I hope you're aware that constant time execution is hard even in assembler(!) since the cache, branch prediction, and so on can all foul up timing in small but exploitable ways. Python being about thirty levels of abstraction higher makes it correspondingly harder. –  Aug 09 '14 at 12:16
  • 1
    I see some problem with making time exactly the same, because even the same branch is not guaranteed run the same time in different executions. I'd rather try to randomize the delay. – bereal Aug 09 '14 at 12:17
  • In case if you know the upper limit for the run time, for example, it takes <0.1 sec in any case, measure the real time and add the delay to make it around 0.1. – bereal Aug 09 '14 at 12:23

1 Answers1

10

Since your question is about security, I assume we can leave aside performance and very naively introduce a minimal time to spend on all possible branches.One way to achieve this is with context managers:
Your problem could be then written as:

with ConstantTime(0.1):
    if condition:
        foo1()
    else:
        foor2()
foo3()

Using a context manager defined like this:

import threading
import time

class ConstantTime():
    def __init__(self, length):
        self.length = length

    def __enter__(self):
        self.timer = threading.Thread(target=time.sleep, args=[self.length])
        self.timer.start()

    def __exit__(self, exc_type, exc_value, traceback):
        self.timer.join()

You would of course have to adjust the actual time to realistic values, depending on what you are doing.

In general you can't be sure your minimal time won't be exceeded by the chosen branch, since Python is a very high level language and you're probably not running it in a real-time OS, but if you manage to cover the average runtime, you should significantly reduce the information gathered from timing analysis.

Leo Antunes
  • 689
  • 7
  • 11
  • Thanks for your answer. It's a very elegant solution. Just to clarify, `0.1` would be just an upper bound because it could be that in some executions`f1` or `f2` last longer than that, right? – synack Aug 10 '14 at 07:57
  • 2
    If both branches are side effect free and not very expensive, it's also possible to just run both in separate threads and join both threads. – Asad Saeeduddin Aug 10 '14 at 07:57
  • @Kits89: yeah, precisely. – Leo Antunes Aug 10 '14 at 09:59
  • @Asad: interestingly enough, that was also my first idea, but then you'd have to make sure you're using multi-processing, as opposed to multi-threading. I'm assuming in the context of security it is more likely that both branches will be performing computation and not IO, so python's GIL comes into play and you might end up with the cumulative runtime of both branches (which might be OK, but would be a slight waste of CPU-time) – Leo Antunes Aug 10 '14 at 10:02