 Everyone, we start session three, hardware obfuscation. The first talk is stealthy, opaque predicates in hardware, obfuscating constant expression, negligible overhead. It will be given by Max Hofmann, which is a PhD student in Bocum. Thank you, and welcome to the obfuscation session. So let's start by talking about what obfuscation actually is. Well, with obfuscation, we take something which, well, looks kind of easy, kind of trivial, and we turn it into something with the same functionality, but which looks much more difficult. And why do we need obfuscation? Well, if you want to manufacture a product, you have a high-level description of it. Imagine some kind of C code or your hardware description language, and you turn it into your finished product, some binary or some gate-level net list. And the way to the product should be really easy. And if someone now wants to reverse engineer your product, so he wants to go back from the product to the high-level description, this is, well, let's say, not that easy, very hand-wavy. And now if we take obfuscation into play, the way from the high-level description to the product should still be easy. But the way back, the reverse engineering should be insanely difficult. Again, hand-wavy, but this is what we want to achieve with obfuscation. So before we go to hardware obfuscation, let's take a look to software obfuscation first. One target in software obfuscation is control-flow obfuscation. Here you can see a control-flow graph, which basically visualizes the branches software takes. You have all your basic blocks, and then they are connected by branches, and you can visualize the branching behavior with a control-flow graph. And if you now obfuscate the control-flow in software, you may get a control-flow graph like this. And this is now much more difficult to analyze, even for a human being. And opaque predicates are a basic building block from software to realize control-flow obfuscation. And an opaque predicate itself is an expression which looks like it has a dynamic value, but it actually evaluates to a constant value which is known to the designer. For example, take this expression, x times x plus 1 modulo 2. And this is actually 0 for every x. But a static analysis tool will not be able to identify this as an always true expression. And this is important to understand. Opaque predicates help against static analysis. And static analysis, well, analyzes solely static data. It analyzes your binary or your gate-level netlist, but it is not allowed to simulate, debug, whatever. This is what dynamic analysis can do. And with opaque predicates, we aim to harden against static analysis. So let's take a deeper look into this example we had before. If I use this expression as an if condition and a branch to foo or bar, then a static analyzer cannot detect that this is always true and will output a control-flow graph like this. However, the true control-flow graph should look like this. We always take the branch, the true branch, and go to foo. Now, how can such a software obfuscation technique help us in hardware? Well, obfuscation should help us against reverse engineering. And a reverse engineer might not even be interested in the whole design. In fact, most of the time a reverse engineer is interested in a small module of the whole design, talking about hardware now. So what we want to do is we want to hide as much information as possible in general so that the reverse engineer has less anchor points to start with, and even understanding small modules is much harder. So if we take a look at this example of a comparator, on the left you have a 4-bit comparator against a fixed value, and on the right we compare two dynamic 4-bit signals. And you can immediately see how the hardware changes depending on which use case we have. And this also helps a reverse engineer. If he encounters this structure, we can easily deduce, OK, there's a comparison with a fixed value. Well, in this case, he has no idea that he cannot see that there's a fixed value because there are two dynamic values. So what we can do with opaque predicates in hardware is we can try to hide the information which is introduced by constant signals. With our opaque predicates, we can make every construction that looks like this, look like this, and therefore remove information the reverse engineer could get. So previous work. There was only one prior work on hardware opaque predicates by Sergei Chigetal. And what they did is they instantiated an LFSR, a feedback shift register. And all these state bits go to an OR gate. And because an LFSR always has to have an hamming weight of at least one, the OR gate will always output one. So this is a valid opaque predicate. You have a moving dynamic state, but the output of this construction is always one. Well, there's a problem with this construction. It's very easy to detect, and it's very uncommon. Detecting LFSRs in a design is kind of easy. It's a self-contained module. But I cannot imagine any use case where you would genuinely or all the output or all the state bits of your LFSR together. So this can easily be found and removed by static analysis, as also shown by Valard et al. And the problem is that we need to have a metric to kind of quantify how good our obfuscation scheme is. And this metric is stealthiness. We need to be stealthy. The problem here is, how do we measure stealthiness? In contrast to software reverse engineering, where we have a lot of automated tools that help us, hardware reverse engineering is mainly done manually. So the human factor plays a role here. And how do we quantify the human factor in reverse engineering? So this might be even impossible, but we can at least argue for good stealthiness. And this is also what we do in the paper. So now let's talk about the main contribution the opaque hardware predicates. We want to be stealthy. So the idea is we use common structures, unlike sagging at isles, so we cannot be detected that easily, or distinguished, better say. And we try to use existing circuitry, because if you use existing circuitry, we don't add new modules, how can anyone distinguish? And the observation we use here is that in hardware, signals constantly change their values. They are switching all the time. In software, you only have changing values if you compute something and store it to a register. But in hardware, all signals, all combinatorial circuits where the inputs change also switch. However, the value, the output value, is only evaluated if it, for example, reaches a flip-flop that is enabled. If the flip-flop is not enabled, the signal can change arbitrarily, it doesn't matter. And this is exactly what we use. We use an existing signal, which has a constant value whenever we need it, but switches uncontrolled by us, otherwise. So how do we do this? Well, let's look at this example. We have two FSMs, finite state machines, and the first state machine starts the second one and waits until this is done. And I assume we need some constant value in the states work one, work two, and work three. Now we have multiple possibilities to instantiate an opaque predicate from our state machines. For example, this weight state is maintained throughout the whole operation of this finite state machine. So while we are in work one, work two, or three, the register encoding this state is constant. So we can use this encoding of this state to generate our constant value we need. Another possibility is to use these three states in conjunction and use only bits of the state register, which don't change in the encoding of all of those three. So if we, for example, have this five-bit state machine, that's transition logic, and we want to create this constant here at the C outputs, then we can do the following. Well, state one, we encode like this and take a look at the red bits. They don't influence our constant at all. So for the next state, we can change these two without changing the constant. And for our third state, we can do this again. Constant's still unchanged. Now we leave our three states where we need the constant and, well, for example, we encode this with a zero. Now our value would be zero, but it's not evaluated, so it doesn't matter. So what did we do here? We achieved very stealthy structure because we use existing FSMs, yeah? And we do not need any additional gates. We just need new wires. Well, in theory, we will look in the evaluation part further into this. And it's nice because it's applicable to nearly all designs. All designs that have FSMs can be obfuscated with this. And even further, the reverse engineer now has to not only look at the data path because the constant is not in the data path anymore and it is not merged into the combinatorial logic. It comes from the control path, from the control logic. So in order to reverse engineer this, he has to reverse engineer both data and control path. This technique is applicable to FPGAs and ASICs. And, well, all together, we now force a reverse engineer to apply dynamic analysis to analyze our design. So what can we do if there's no suitable FSM? Maybe we have asynchronous FSMs and we cannot use one to encode our opaque predicate. Well, we also show in paper in detail how to add an FSM-like module to the design which after reset stabilizes in a done state, like after some clock cycles, and you can use the encoding of this done state to generate your opaque predicate. And this is also still common, right? We just add a new FSM, basically. And FSMs that stabilize are also quite common. You have a done state which is only left by resetting your state machine, for example. So let's look at some case studies we did to assess the overhead we introduce. Just very briefly, we have two scenarios. The first one is a present encryption core which is trojanized with a fixed, with a plain text triggered fixed key. So if a specific plain text arrives, then the core exchanges the user supplied key with a fixed internal key. And what we did to obfuscate the two constant values, the comparator value and the fixed key, we used our opaque predicates there. Note that this does not like hinder detection by other techniques, just you cannot scan any more for any information that is introduced by constant values. And the second case study is a subverted RSA core. There's a kleptographic attack. All you need to know at this point is that there are two constant values, an adversary chosen exponent and a modulus hardcoded into the design. And we again used opaque predicates to hide them. And these are the results. Strategy one is always obfuscated with existing circuitry and strategy two with additional circuitry where we always edit an FSM module of the average size of the other FSMs in the design. And what is really nice to see that we achieved super low overhead. And in one case, the additional wires we introduced even had the synthesizer improve the design and save two flip-flops. So one more application, as these two are like hiding Trojans, they are offensive is a defensive application to secure water marking. So what's water marking? A water mark enables a vendor to identify IP theft. He can embed a water mark into his design, then analyze products in the market. And if he finds his water mark but the vendor of the other product didn't license his course or something, he can file a lawsuit or something. And Schmidt et al proposed a water marking scheme for FPGAs where they embedded the water mark into the lookup table configurations. So how's the lookup table configured in an FPGA? It's configured over its output values. So if you look at this lookup table, all the inputs which are supplied later will lead to one output bit. And if you change this bit string here, you immediately change the behavior of the lookup table. So in an FPGA, this bit string which encodes the outputs is the lookup table configuration. And this lookup table configuration can be read from the FPGA bit stream. So a vendor can easily extract all the lookup table configurations from the bit stream and look for his water mark. And the idea of Schmidt et al was to fix some of the inputs to the lookup table or to some lookup tables to ground. Well, what happens? The input combinations where either I2 or I3 or I1 will never occur in practice as they are grounded. So all of these input combinations which are gray here will never occur in practice. And now the configuration bits for the lookup table which correspond to these input combinations can be used to insert water marking bits. And if you now use multiple lookup tables, you can embed an arbitrarily large water mark in there. So what's the problem with this? Well, a net-test level attacker was included in the attacker model. And a net-test level attacker can trace all the ground connections to lookup tables. And now he immediately has all the candidates of lookup tables where a water mark may be embedded. And he can just zero out or randomize all those bits which could be used for a water mark. And the vendor can never find his water mark in this device anymore. Note that this is also possible from... No, sorry, sorry. And the solution we propose here is use our opaque predicates instead of the ground connections. So if we replace them with opaque predicates which output a constant zero, then the behavior will be the same. We will still have these input combinations never occur in practice. But to an attacker, they now also look black. They are not grayed out anymore because he cannot see that these are constant zeros. So all of these combinations seem to be possible in the circuit and the water mark cannot be removed via static analysis of the gate level net list. So let's conclude. In our paper, we proposed a novel technique for hardware opaque predicates which is applicable to both ASICs and FPGAs. And it's a kind of strong technique. It's very stealthy because of the existing circuitry and the common structures. And we have an extended discussion of the stealthiness in the paper. We also propose two instantiation techniques with existing circuitry and new circuitry. And we also provide a practical evaluation both for the malicious use case and also to show how can we defend, like how can we mitigate existing attacks as in case of the water marking scheme. Thank you. Thanks, Max. We have time for questions. Is there any question on there, Ingrid? Thank you. I have a question. Synthesis tools will also do state assignment. And so how does, I mean, they might optimize away everything you add. How do you deal with that? Well, first, our evaluations already show that this does not happen, let's say, to the full extent. Otherwise, our obfuscated designs would have resulted in the same designs as the genuine or unobfuscated ones. Furthermore, these synthesis tools, they cannot deduce meaning, let's say. This is what I meant with human factor reform. The designer knows a specific meaning. Well, I want to create a constant value with this, but the synthesis tool would have to do the same a reverse engineer would have to do now. It would have to analyze in which states, basically, the registers where our signals go are active and deduce whether throughout this active time the signals are actually constant. So if our synthesis tools could already, like, remove our obfuscation, then this would also be an indicator for reverse engineering being trivial in this case. And I guess it's a good indicator for the other way around as well. The synthesis tools, which really spend a lot of time on synthesizing, cannot remove this technique easily. I hope this answers your question. You have time for another question? No question from the audience, I do have one. I assume that you inserted manually now. Sorry? You inserted manually your predicate now, right? Do you see, how do you see inserted automatically? Is it extremely complex thing? You can handle to some extent. Can you say something in the future direction? So I think this can be done automatically to some extent. I guess we need to kind of annotate our code for that. We would need to extend the synthesizer so that we can say, well, I need these three states to yield some bits, which are constant, for example. And then the synthesizer can, of course, like choose the encoding and do all the optimization internally, but then it ensures that there are exactly like the most significant bit of our state register will be constant in these annotated states. And then we can wire this most significant bit to basically anything we want and can rest assured that it will be constant. So I think by just annotating and then minorly tweaking the synthesizer, this can be automated. And do you see any major difference between the FPGA approach and DESIC, because I saw you say it's similar, but you did experiments only on FPGA so far. In theory, they should look like similar, but... Yeah, I mean, experiments on an FPGA because they are more accessible and easier to change your tests. However, the technique is completely separated from any technology. We just assume that we have our state machine that uses a register to hold its state. And that's all we assume, basically. And this is true for both FPGAs and ASICs. However, for ASICs, you would actually introduce wires, let's say, real wires. And for an FPGA, you would have other routing through the internal lookup tables and switch matrices and everything. So the synthesis tools would kind of work in your approach differently, but how you would realize this is completely platform-independent. Okay. Then thanks, Max, again.