 All right, so we're gonna start off this day's talk with sub-turing machines. We've got falcon duck star here So please welcome him to the tour camp stage Let's get to see the stage again. And of course, I'm excited. These talks are being sampled cast now. Hello, lol Or is that k wall? I don't know if that's a actual call sign, but okay. Anyways, I'm sub-turing machines the end of unknown unknowns this is Composite of a lot of information that I've been working on for the past few years with much of the language theoretic security research crowd So I owe many citations to to Sergey Bradis to Meredith Patterson. You got married on the stage two years ago I think and to so many others The rest of the information from this talk comes from our experience attempting to build our camp project this year and the Phone switch that ended up not quite being ready But we learned a heck of a lot about electronics design and the mechanisms in which we designed it We're intended to convey a large amount of the sense of language theoretic security So with that With that I'll just quickly reintroduce myself. I'm a security consultant at Leviathan security I'm still working on my my thesis at Athabasca. I just trying to finish up here And I'm change health systems engineer I I Also got a paper published since the last time that I was here It's called the seven turrets of Babel a taxonomy of Langsec errors and how to expunge them what this paper does is it taxonomizes The various issues that that language theoretic security posits exist in programs not as individual bugs But rather is design patterns because language theoretic security is a design pattern driven methodology This paper is available freely. It was published at IEEE Secure development, so it's You don't have to go through a paywall to access it on some of the content that I give is also available in that paper merely because it is Necessary background, but so without further ado I know that every time we come up and talk about Langsec There are a lot of people in the crowd who are already familiar with it Studied it extensively in school I'm sure and then there are lots of people who just kind of wonder what it is and how they can make it work for Them so language theoretic security as my favorite way to explain it is a lot of math We do the following math for example We use this particular piece of math to describe a finite state of tomaton a finite state of tomaton is a machine this machine is composed of Some elements, I won't bother going through this math This really is not necessary to understand it at all a finite state of tomaton is a machine It has a number of states there define transitions to each of those states based on each input character So it processes an input character moves to another state And then at the end if the state is deemed to be an accepting state It decides that that string is real if that states not an accepting state It deems that that string is not good and it rejects it You might recognize this from the way that we typically implement regular expressions because of finite state of tomaton and a regular Expression are precisely equivalent and so a regex match is Basically just the simplest kind of parser that you can get if you define your regular expression correctly You'll be able to parse the input and check that it's valid correctly before you even do anything else with it The only thing that you have to know is that you're non deterministic finite a tomaton That is your regular expression all of our things have intimidating sounding names, and they're not really intimidating at all Excepts or rejects it hmm And that gives you the power of a whole set of assumptions, right? You can now assume that because it's valid all of your logic code can just assume that everything is in the right place that The input itself is valid if your specification is strong enough that that input is non malicious and You're good to go So that was about three of our design patterns at once by the way But mostly it was about limiting complexity and about validating input before you parse it Which is the opposite of shotgun parsing? So importantly language theoretic security has absolutely nothing to do with the programming language in which you're writing Unless the programming language is really too weak to support the things you're trying to do which is another problem entirely It's more about how the machine that you built works the machine that emulates your input more narrowly A lot of it is about what class of machine it is and how well that machine is specified. So here's a machine Um, I don't know if this is gonna come up clearly. Maybe I should have blown the diagram up a little bit Can anyone actually see this? Okay, good So this machine is a little device and what this device is is it's part of the phone switch that we have about 85% built and The purpose of this machine in general is when somebody picks up a phone or some other event happens on a phone line It's supposed to figure out where in the switch fabric that line is supposed to be connected So you see this the circuit has a couple of things in it. It has that 157 Multiplexer over there for channel multiplexer and it has a latch over there It has five diodes and actually I cut off the bottom because it's just LEDs That's and a few passives are actually the only pieces of complexity in this machine. How many bugs do you think it has? Lots okay, um Information theory tells us that there can only be so many bugs because this is this Representation my code is an encoding of the bugs that I write and so if my code is too small then there can't be that many but there's at least one um And it's not just because hardware is hard It's because state machines also tend to have emergent properties when you have a representative complexity We we tend to arrive at functionality that we didn't expect and that's really the essence of a bug on which We built the language theoretic security paradigm Um, so this is this machine computes three functions. It has its outputs kx one kx two kx three Those are the state of the relays that govern where they the input is connect switch The where the input is connected into the switch. Sorry it has Seven inputs the inputs are whether there's a call somewhere in the switch for the line three times and whether or not There is free space in the switch three times and whether or not the phone is off the hook so with that function from Six arguments to three outputs or those three functions each from six argues each from seven arguments to each of one output Is effectively a program even though it's effectively this simple So The the essence of the design principle that we tried to do with this was that this this circuit is an effort to limit complexity instead of using a microcontroller and writing a whole bunch of if statements and then having to figure out You know how to do the timings correctly and wait for all of the the various lines to fall into place We figured well, let's just build a circuit. It's a very simple machine. We know it's complexity. It's bounded Just for curiosity's sake with a little machine like that You can see it has one latch and there's really very very very little else about it other than that one multiplexer Which is just a function of and And the diodes which form an orgate how complex or what language class do you suppose that might be? Do you think that the regular expression would be enough to express the sort of computation or is it more than that? Because it has state. It's good to be awake. So I Think it's regular I think it's regular and the reason that I think that is because it has only got Four bits of state and actually only three of those bits of state are used It can't nest it can't really do anything other than like its entire Output set is finite, you know, it's not even technically regular. It's within regular. It's it's a finite grammar I could easily make a truth table for this component and when you're designing these things That's where you want to be but as we learned you have to include your timings in that truth table too Or it's incomplete and you haven't specified it fully so more to follow on that Um The essence of language theoretic security is about limited complexity and deterministic behavior So with our design here, you couldn't possibly say Glitch it out and reprogram it and there's no serial interface by which you could send it new firmware and cause it to do weird things And and there's certainly going to be no buffer overflows you can only pick up the phone so many times before the phone is picked up Um So it's it's deterministic context for your less complex it certainly falls within that because The boundary that I'm drawing here is as long as you can Kind of bound the amount of state that you have in in this machine of yours Or at least you can define that if things are nested they're only nested kind of linearly They don't depend on things that are across the tree structure after you've parsed out your your XML document They don't like everything every every notion of validity everything that matters is Findable from the place where you are in that document. You don't have to go and parse the whole document and then look somewhere else and find it That's the level of complexity that we're looking for So the point of the exercise in the shady switch once again to recapitulate was to limit the complexity available to phone Freakers you don't expose power to attackers when you don't need to you shouldn't make your machines any more complex than they strictly need to be So it's not about the language that you write in it's maybe about the language that the compiler is written for though because when you When you write a program it really is a virtual machine or a compiler for your input and It's more it's not about a general theory of computer network exploitation We don't hope to solve every possible flaw ever with language theoretic security and certainly we don't hope to Solve flaws where you were doing something, but it wasn't necessarily the right thing to do like build a phone switch We're not trying to solve memory safety in general with this It solves memory safety in a few specific ways But you might notice that it discusses memory only in abstract terms this language theoretic methodology It says things like we have this much state It doesn't discuss how big the buffers are be or how they're going to be allocated or what your heap allocation mechanism might be And really above all else It's not a call for programmers to be more careful. It's a call for people to use different design patterns So that they don't have to be more careful The security industry has for years had this thesis that if only developers would remember not to X not to Use unchecked stack based buffer copies not you know not parse their input without Deciding whether it's valid. Oh, well, I do ask them to be careful not to do that But we asked them to be careful once not be careful a hundred thousand times in their program that every little thing meets each and every one of It's preconditions because that approach has always failed. So this is really just a call for a new a new methodology It's about input processing it's about data interchange it's about protocol specifications So you write a program and the program kind of per force ends up having a protocol that it ends up parsing Even my little circuit that I showed you before has a protocol that it ends up parsing each of its seven input lines have a specific meaning And this this constitutes a protocol. It's not a serialized protocol. It's a parallel protocol There are two to the seven symbols in my protocol and one element, but nonetheless As soon as I laid down the design There a protocol also sprung up and if you're not deliberate about what that protocol is then it tends to be a piece of Emergent complexity, which is how we get bugs It's about data interchange as well because that system has to interconnect to other systems And if those systems do unexpected things with the pins for example What happens if I have a hundred Hertz AC at five volts on on the line pickup pin because you're really just that quick on the hook switch Does it still work? Well, you have to account for that and if you don't design the protocol on purpose As we found You you will tend to end up with emergent bugs in this case. It expresses itself as timing issues It usually expresses itself as things like your program putting itself into a strange unexpected state and crashing or giving away data You didn't intend it to give out And so those two things are supported by input processing we need to make sure that in my case The input processing handles the pins correctly and polices the timings in in more software related cases We need to make sure that it is policing that the protocol that it's getting is actually the protocol that it expects That there's not a see on that pin is roughly the equivalent of this is actually a PDF document and not an executable Actually, sorry. I misspoke PDF documents are almost always executables, but they shouldn't be okay So it's a software engineering methodology. That's all that is so Let me talk a little bit more about the background on this product and this project and this little circuit that I'm using as As an example of how small a machine can be before it starts to have bugs if you don't follow this methodology well This project that we had to build this phone switch essentially our central idea our initial idea was let's do this But let's do it without microcontrollers Let's do it like the equipment that I was talking about two years ago when I was here All of the number one number five crossbar switches of the bell system, which were built with relays and vacuum tubes Entire programs. There's a device called the marker the marker decides where everything will go and it does this because it's a cabinet It's a cabinet about about that big full of relays So we figured why can't we just do the same thing? It'll probably be about the same number of components and about as complex But it'll probably be a few orders of magnitude less expensive because we have transistors now So let's do that So we decided that much like that system There would be no microcontrollers unless they're special microcontrollers They can't be reprogrammed and the outputs are easily described as a function of the inputs So that's basically a shortcut. Oh, I need an ASIC. I'd rather not fab an ASIC Here's the microcontroller that exactly does this one thing And then those verified So it turns out that you can buy chips like that for many common purposes And I will get into that later because it's another example So the other kind of idea with this switch is that the state should fit in a reasonable regular expression We don't want to keep track of a bazillion things everything complex like for example billings not not in there It's outsourced somewhere else So Just kind of to tie that ancient background skip 60 years and come straight to the present It turns out that just like those old systems with the relays storing all of the state each relay governed It stores a bit of state about like is this lineup is this path in use which thing is next in the priority chain those look a little bit like bits and RAM and If a computer has one thing that's finite, it's bits of RAM And so it turns out that all working computing models are trivially regular or actually trivially finite You can technically enumerate all inputs and outputs and all state of the computer much like to simulate the universe It would take you a device approximately as large as the universe or greater This turns out not to be a very practical methodology. And so sometimes When when discussing Langsack you you always get a couple of people who wonder Well, I mean sure this language appears to be an XML document But since there's only five of them that it can be is it really XML? Is it really that bad if if I have this bad thing just limit it in some way and the answer is Probably it depends on whether or not the description that is a finite state machine or a finite language is Actually a useful description of the way that the machine works If someone looks at it and they're like, oh, this is a truth table. It's I know how to work with this It's great. It's a specification people can work with that If it's like here's a list of all possible configurations of bits on this hard drive You can't reason about that you you technically could reason about it a computer could reason about it in a very long time It could you could indeed simulate all possible states of a program and figure out whether or not any of those states represents the state of possible exploitation and this is largely what we do with fuzzing and Fuzzing doesn't verify things So no the answer is just no if you want your verification to be doable within the year Or the decade you probably shouldn't rely on trivial representations of Things that do have actual complexity There's also the question of whether or not the enumeration is sound So for example even with the circuit of this size It was challenging for us to enumerate all of the possible states in which it might be to figure out where bugs might lie Imagine doing that for an actual program So what's left after you take all of the programmable interface controllers and all of the arduinos away in our design is a whole bunch of 7400 series logic and One interesting thing about 7400 series logic is that working with it feels a lot like functional programming Which I'll get into in just a second No, no not really no because we've just deemed every variable to be an input line. The heckle was no side effects So There can be but they exist at the electrical level So for example as I was saying language theoretic security is not a panacea for information security in general My methodology could not find row hammer the shady switch could potentially have row hammer Although everything is so coarse and practically DC that I don't know how you would There's actually a timing attack in our current design by the way So good heckle um So All computing models that can technically be it can be implemented are technically regular because if they had infinite amounts of state Which the next one up does it has an infinite stack? You can't actually do anything with it But this really doesn't matter because what we want to do is we want to reduce vulnerabilities by reducing complexity And if we build a system whose description is so complex, this is basically equivalent to building a system Who's input grammar is so complex? So with that let's consider some more of the fundamental Things that fell out of Langsec. We figured out very very early on that the most important thing in a language that was able to Allow software engineers to use a language theoretic security methodology is that this language has strong type systems So in other words unlike JavaScript, you can't simply transduce types to other types implicitly and just hope for the best Unlike Python Programming a lot. You shouldn't like pickle things and then write them to a file Hope that it remains unchanged forever in stone and then unpickle them because we know what happens when you do that strong type systems mean that every time we take in some input and we've parsed it it acquires a type The way that this is expressed in this programming with solder idea is that each of these pins Fundamentally has a type and I'll get into that in the next slide But the strong type system is really the back one of this because the strong type system allows you to make guarantees about things like yes This part is validated as to that It allows you to make guarantees such as of all of the special cases of this function Because our input parameter was this it's this particular special case And you can make your type system as granular or as coarse as you like But merely using strings and integers is simply not enough The strong type system has to have sufficient complexity in and of itself to allow the compiler to verify that bugs do not exist So what do I mean by this? Well, I mean that Imagine all of those scenarios where like you have a unicode string and then you think that it's actually the utf 8 string is Just an ASCII string. So you just process it as a byte array And you're good to go right? No. No, you're not What you have to do is verify it and so what's better than reading in the unicode string and having a byte array and then just Outputting a byte array is to have some kind of string type that guarantees to you for example Whether or not the string is actually a continuous array of bytes and to get there You had to parse the entire unicode string and figure out whether or not there are any characters that are larger than 8 bits In it before you can make that guarantee That sounds like the perfect job for a constructor doesn't it? Anyway um Constructors are also awesome because they prevent if you parse in the constructor You can show that you don't have the shotgun parsing anti pattern in your program. So shotgun parsing I alluded to earlier It's this thing that we Is this thing that happens when you parse and then you do a little bit of logic And then you parse some more to figure out whether the rest of your input is valid And then you do a little more logic The reason that this sucks is because at each step that you do a little more logic Your program is going into a whole bunch of states in those states Potentially touch valuable data that you're trying to do the parser is a toy the parser can do two things It can pass the input on or throw it in the garbage the rest of the program is where your entire threat model lies Unless your parser implementation is bad and I'll get to that in a minute, but um the the central idea here is that if You start to parse and then you do some logic and then you start to parse with this shotgun parser The effects of malicious input spread throughout your program state much like the pellets out of the end of a shotgun And it's really impossible to clean up the mess afterward so we avoid that by making sure that we have strong type systems and Hopefully hopefully we architect them correctly there are methodologies around that that I won't get into immediately, but The other way around this and I was just talking about this in object-oriented terms is that Remember how I said our deterministic finite automaton is kind of like a function and how our circuit has a truth table Which is kind of a thing that functions have Functional programming is also perfectly fine for this If you have a function that transduces from input to verified output that is perfectly as good as an object with a Constructor the point is that you know that when you have an object of a particular type that the things that are in that object meet The criteria for validity of the thing that that type is Because the shotgun parsing anti-pattern and effects like if you remember someone's ability for example to completely hack stratum zero NTP servers by simply sending them a null key and asserting that the time was 1970 If you don't reject this this invalid input document with that null key before it gets to somewhere dangerous It might actually blow up So let's find some types Um, I mentioned before these two components. I don't like upford or symbols I'm in the middle of getting rid of it. The component on the left is a mux the component on the right is a is a latch and For people who haven't worked with these particular things before because I know that this is getting to be a little bit arcane on a multiplexer is a device it has kind of Input it has a series of input pins and then it has a selector for which of those sets of input pins goes to the output So this one has two sets of input pins a zero through three b zero through three and it has an s pin Which selects which one of those two is valid? The one that is selected is the output on y1 passed directly through buffered So what is this component? If you were to imagine what that component is in a program is There may be some logical structure that that might be it's an if it's just an if statement. It might also be a case statement depending So if you wanted to try to consider well, how complex is this program one exercise that I found that was very interesting is How much of this program could just be implemented by by non clocked solid-state logic? The thing that's after it a bunch of diodes those form two or gates The reason that this is true is fairly obvious So if any of the inputs on the left is high the output at the end of the diodes will also be high And then that goes into my latch So at each stage of this I kind of have some types don't I so in the beginning I have I have about seven types. I don't know if that's legible the pins are labeled I of x1 through 3 Not s of p1 through 3 and s of x and what that means is I of x1 through 3 is A line that basically is one bit. It's a one-bit type It's is there an incoming call on this line so I could model this as a type system and I could say This input line this bit it has meaning beyond one or zero it has its context, which is Is there an incoming call for the line that this piece of logic corresponds to on path one two or three? So the type has a value it's one or zero and the type is put into a particular place in our state machine Pin one two or three so the if statement can also be described as a function of course because concepts and The output lines y zero through three also have a type Now you could model this type as being the same as while there's an incoming call for you on this line But that's actually not correct because the function having processed it gives it a little bit more meaning and so remember that our our data type is still Boolean we're still one or zero but The function of this multiplexer has said If the line is down the path that we should be connected to corresponds to the path that has an incoming call for us If the line has been picked up and there's no incoming call yet The state actually if the line has been picked up we'll get to the if there's an incoming call bit later Then the state should be the first free path So That's what it does if the output is If the phone receiver is down the output is whether there's an incoming call on pins one through three in In y one through three and if the receiver is up then y one through three indicate whether or not Path one through three is free So really what this is is it's the type of should we be on this path data? So this is a good example of how type transaction can work Without actually needing to create an object for this I won't go into Absolutely all of the rest of the circuit, but I will discuss the timing bug And the timing bug is merely this so maybe a little bit more about the circuit You might notice that pin zero on that multiplexer has nothing to do with that It's still selected on whether the line is up or down But it's actually not having to do with inputs ix of one through three and s of p one through three What it has to do with is latch enable on the 74 sub hc 75d latch. That's right there So what latch enable does for those who have not recently used a latch is while latch enable is high The output of the latch is whatever the input of the latch is while it's low The output of the latch is whatever the output of the latch just was ignoring the input completely And that's a latch it stores state this latch stores four bits of state and Whether or not the latch is transparent Meaning whether or not the latch enable pins are high depends also on if or if not the receiver is picked up Where's the timing bug? Does anyone see the timing bug already in this? okay, so There's two components here that are used for timing R30 and C30 resistor and capacitor the resistor is simply a two-kilometer resistor and the capacitor is a 10 nanofarad capacitor Which means that to charge up to the gate potential of the latch enable it takes the capacitor about maybe seven Sorry, yeah about I think I'm gonna say seven nanoseconds to charge up Now it turns out that this is actually the wrong thing to do um The reason that we need to have timing control here is because we can't simultaneously have parallel processing goes on that says Change all of this state and also immediately latch it in at exactly the same time that simply doesn't work We found in an earlier iteration of this chip that actually we ended up exposing of this design that we ended up exposing the internal timing rules of this this latch And pass one and two did the correct thing But path three did not do the correct thing and when we put the timing capacitor in and exacerbated it why? Well, here's that emergent functionality. We didn't know that our implementation was faithful. So what happened is We had these pins Ix of one and s of p1 and The same thing that switches which one of those is on the output and goes into the latch It happens is the pin That is deciding whether or not that will switch over to being path occupancy. And so what happened is specifically For path three when you picked it up It would shunt you over to the first available path instead of actually picking up the call Because it had already switched over to oh you want to pick up the phone the state's not latched in yet So that's what happened Hmm This is why it's really critical that when when we build programs in general We have a very good idea that the framework which we're using is correct because for all of the truth tables that I can draw And for all of the validity's, you know, I can put a state of outputs k of x 1 through 3 at t minus 1 into my truth Table, but if my circuit is wrong and the timings are backwards that delay capacitor should actually be on the other three lines That are switched or it could simply be on the s lead into that multiplexer to delay the state switch And then I need another multiplexer to do the state switch before either of those designs would work perfectly Well and do the timings correctly and then the platform would be right and then all of my verification would be working This is the same if you happen to be using a library To parse XML like for example sacks If you don't configure that platform correctly and you're using an old buggy version of it No matter what you do in terms of writing very good xsd documents that constrain XML into a Context-free system and make sure that every tag that's in there is one that you know how to parse for all that You're probably still going to be vulnerable because your parser is vulnerable and so this this notion of the platform Becomes very critical if you don't have any reason to believe that the platform is good So I don't do this very often because I find that it's it often only succeeds in intimidating people But I'm going to talk about parser combinators A parser combinator is not a word that I like to use because really a parser combinator is just a parser generator library Does that sound a little less intimidating maybe? No, oh, I'm sorry. Okay. Well, what it does is it allows you to take a specification document Like a BNF copied out of an RFC was the dream And just generate a piece of code that parses it correctly so that you don't have to worry about whether or not you're you're writing a Correct parser library when you program This is become a little bit of a mantra it much like generate your passwords generate your parsers I kind of built my own parser sort of here for for the constructor if you will of K of x3 and it turned out to be wrong Based on things that we didn't even anticipate or consider while we were building this design I wouldn't recommend any specific library to do this because I mean a lot of them are still very academic in purpose But working implementations have been built and these are not new concepts Yak will do this we've been using them for a while to to parse and process the more complex input formats that we have and most importantly take the specification that we have and Generates into any old language that we intended to program in Working implementation of that parser that is exactly equivalent to its implementation in all other languages So okay, I already went over this We found types from our circuit We found that there were Boolean types and there were floating point types the floating point type is the potential on this line here Line le one which goes into the latch enable pin And this kind of it gives us two emergent models of how the circuit works So are the types just Booleans while I wish the types are analog signals And so our program in those analog signals has to be correct and the easiest best way to do this is to crowbar it such that Every time you you have a Boolean signal You know that the the corresponding analog signal that's supposed to do it is correct because you arranged your timing capacitors correctly That constitutes the platform and when we do this we can abstract away this notion of well What is the gate potential and is there a difference between like each of these various lines? In how soon the gate potential for the latch enable rises or falls to the level that it should be to be the appropriate state um Yeah, I already went over all of the rest of the things that I was going to discuss on this slide, but um So what happens as well is it's an interesting thing to think about if you confuse I of x1 for not s of p1 For example, we had a solder bridge or we didn't lay out the board correctly And those two lines are bridged or those two lines are swapped Well given the framework that I just described effectively what we did is we violated the type system These two things are basically exactly the same when you cast unsafely The thing that you are doing is is swapping leads to places where they don't go and the thing where the input went It doesn't really know what to do with it because it doesn't mean what the sender thinks it means And so this is another example of how you can see in in the hierarchy of language theoretic security issues They really do kind of blend together and I'll I'll get into that I've stolen a slide from when I gave the paper for later and I'll discuss which of those ones blend together But when you apply this methodology It's important to consider That the the design fly is a design fly. It's not one individual bug that you can just go and fix And in fact when we tried to do that by putting that capacitor there We noticed that it actually did the opposite of what we wanted to make the circuit more unreliable because it let the state settle more in advance of when it was supposed to flip over so This is basically like an unchecked cast This is basically like my platform sabotaging me and When this sort of thing happens, it's really not the machine that I built anymore It's got some kind of electrical flaw or the type system is broken is basically the same concept as the point that I was trying to make with that whole diagram And so now I'm going to spend a little bit of time further breaking down the distinction between CPUs and programs There's really no distinction at all There's no distinction at all in practice and in fact one thing that we found is that With with most modern processors you have a whole heck of a lot of microcode and the microcode can occasionally have bugs in it Well, is the CPU executing your program is it executing x86 or is it executing the microcode on x86 as input? You know the program has to be understood at each of these levels and so really the CPU is just a platform for implementing the CPU Hardware acceleration basically works in the opposite direction I take a thing and I make a little circuit like this which without any iterative computation processes a complex function Hmm Well now the program is in solder So the opposite of microcode we've we've moved the other way And it turns out that because these two transitions could be made at infinitum if you had enough patients to lay out those boards It turns out That there's no real distinction at all So this is one of those microcontrollers that I talked about earlier. This is a chip that decodes DTMF You'll notice that even though it's a chip it comes in in a package. It's a soyk. It's fairly large It has a whole bunch of algorithms in it. It has the digital detection algorithm. It has the steering logic These sorts of things Mean that what this chip is is it's actually a microcontroller and yet It was engineered such that you can have a data sheet that has a truth table for it And so this is a perfect example of a microcontroller that we would accept into this design that's supposed to be built using language Theoretic security principles and as simple as humanly possible That complexity can be abstracted away if you verified it and ensured that in fact For example these outputs Q1 through 4 is actually a BCD representation of the input And it's not possible for me to say strobe Q1 and program the chip We would be comfortable with that because it doesn't expose the internal complexity of that microcontroller Existing and the design is small enough that it's actually possible to verify that it's correct And now suddenly we can use it as a building block. I'll get into the danger of using building blocks in a little bit, too But so when one wonders as well From the previous slide is this chip a chip is this chip a program What is its complexity class? Is it a finite state of Tomaton? Yes, and it's it's finite Perfect even though the computational model in it is a microcontroller We threw all of that complexity away by having a very strong type system and control flow diagram and a protocol for it And now it doesn't have turn complete computation within bounds anymore It's just a transducer from input signals in analog types to output signals and digital types easy You can tell that it's regular because it acts like a chip and it has a truth table anyway So let's talk a little bit more about emergent complexity that MT-8870 is vastly more complex than my thing So I mean maybe it is good to have some concerns about it, but This circuit is small enough is large enough. Sorry to have some emergent complexity even though it has only seven real components and Five of those components could easily be condensed into two if I were willing to buy a couple more or gates um So what complexity class is my buggy implementation one wonders The implementation with this capacitor C30 in there delaying the line that should actually be happening in advance of everything else It's really hard to say isn't it um, I mean, it's difficult to imagine how you might make a regular expression that parses for example, um, the the problem with this circuit is that The effects of that capacitor don't mean that the functionality is deterministically going to make sure That that's when you pick up the call on path 3. It doesn't instead dump you right on to path 1 What happens instead is it depends on like how charged up C30 is whether it does that picks up the call picks up some other call Whether the latch Has has time to settle in all three states or only just a couple of the states or only just one of the states Depends very much on what just happened to the latch the voltages input to the circuit several other factors It reminds me as well of another issue that we created earlier in the design We intended to sense whether or not a line was up by using an extremely small amount of current so that we wouldn't disturb anything else on The line and so what did we use but we fed the line into this loop detector using a 22 22 mega capacitor and then we covered the 48 volts down to 5 with a zener diode and then Inverted it with a 12 volt op amp And this was how we figured out whether there was lines is whether there was a return voltage on the tip of the line That we'd previously sent out on the ring well that 22 Megam capacitor was just a little bit too large and Unfortunately on what we made for this because the current was so small was actually a primitive capacitive touch sensor if you waved your finger close enough to it It would think that the line was up Complexity class of that machine. We don't even know what its inputs are. We thought it was the pins So it turns out that this isn't just something that happens to people who try to be electrical engineers But don't actually know what they're doing There is a model for an x86 processor I don't remember what it's called, but it's an open-source model and academics love to use it And you can kind of just like take this model and compile it its phd l and you get a working processor at the end And you can change it up Or you can do what these folks did this was a paper presented at IEEE Oakland in 2016 And it's a paper that I dearly love and once I describe the exploit you will probably love it, too The paper is called a to analog malicious hardware in case my text is too small and the TV is too reflective and What this paper is about is they took that x86 model that giant complexity machine and They compiled it into the HDL shore, but to simulate an untrusted fab They drew some extra traces on the plates before they got laid down into silicon They drew two things They drew a resistor and they drew a capacitor, and then they drew a few traces to connect them all up, so I'll briefly sidetrack here and explain the privilege model in x86. They're effectively two privilege modes There is privilege mode user or ring three and there's privilege mode kernel, which is ring zero if you want to do certain instructions like change the memory model or like output directly to a hardware device or Access kernel memory without worrying about the page table You need to be in ring zero to do this So if you are trying to explain the kernel or if you want persistence on the machine or if you want to update the bios It's really really really handy for you to be running in ring zero This is a two-bit Components of the not the flags register, but a related register that it's the segment register in x86 Specifically those two bits of the segment register For the code segment in x86 even in 64-bit mode dictate whether or not you're the kernel or or you're the user The bits must be exactly equivalent Basically, nobody used the the other two in between Zero and three, so they're basically gone and not usable, but So what components would I draw on something if I wanted to on command be able to get down into kernel mode? Well, I can draw on the chip. Of course. This is accessible to me What the researchers did is they took the divide by zero line out of the out of the exception Part of the chip and this is just a line. There's a pin on the outside of the chip for it and so they put a resistor from from the capacitor to ground and Then they basically just arranged it so that the divide by zero line was connected to this capacitor Which was connected into a knot gate that was somewhere. So sorry, they didn't draw just to Which was then connect or sorry an XOR gate which was then connected to those two lines So this is fun Now if I divide by zero enough hundred thousand times I'm in kernel mode for a while until the capacitor discharges and then How do you find this? You don't even know what your inputs are It turns out that divide by zero is now something that's a function Input to what the current privilege state is but only if you do it a few hundred thousand times So this exploit is an analog malicious hardware version because it doesn't fit into the digital Computation model much like our problem with you picking up the phone and then accidentally if you do the right thing with the hook switch Getting shunted to a different path And listening in on someone else's call This was an issue whereby the emergent functionality introduced new inputs of completely different types to our program that the attacker Understands, but we didn't anticipate existed and so aren't part of our security or verification model The reason that I love this paper is that any method that you would use to verify whether the x86 processor is correct Would pass in the presence of this thing Nobody is going to say well What happens if I strobe every single line on this chip a few million times you'd never finish testing? This is one of those scenarios where we're trying to enumerate every possible state of the machine The other reason that I like it is because if you've ever had the opportunity to compile something from the HDL into Something that a fab can create You might find very quickly that the thing that comes out of the compiler is not really amenable to It's more of the class of let's enumerate every bit on this hard drive and draw a truth table trying to figure out backwards Then then it is like trying to figure out what a small circuit does So yes writing specifications is not that hard Just try to compiler for them. Just make some machines just You know write a compiler from the implemented program back into its logic and verify it that can't possibly be that hard, right? No, so This is why I spent a little bit of time talking about parser combinators earlier Life is so much easier when you can work with the protocol specification Which is a high-level logical entity that you intended to have then when you have to figure out from the program or God forbid from the compiled program What its input languages and what all the things are that it will accept in which states that will put the program into and whether or Not there's discrete parsing and unparsing stages It's way better to do this stuff in advance and that's what language theoretic security spends so much time advocating for Because every time you write a program you have an implicit input specification every time you lay down some circuits You have an implicit truth table And you really want to avoid this danger where much like this oddball processor that has the divide by zero thrashing lead to kernel mode Your design happens to be one of those unique few that has the bug So can't you just determine whether the dies in the VHDL match to figure out whether or not the analog malicious hardware Trojan is present the answer is no if you've ever compiled a program or you you don't even have to have used VHDL to do this if you ever compiled a program you Might notice that each time you compile it it has a probability to be slightly different There's a lot of non-determinism minor changes in your code can create major changes in the output so no You can't simply take your your code and unless it's net and even then run it through a decompiler and get exactly what you had in The beginning and verify it This isn't really a viable mechanism of checking compilers You have to go through all of the the things that the decompiler gets and figure out whether they actually match Or you would have to take the net list that you find from looking at what's on the die And then figure out if that net list has a material deviation because it will have many deviations But whether it has a material deviation from the program It's way better to verify the compiler once preferably if somebody else verifies the compiler and then use that one And that is the essence of why generated parsers are better We can verify that the generator is correct once and then we know that every time it outputs something it outputs things that match the specification So it could be as easy as just figuring out whether or not our compiler worked by working backwards But it turns out that this is actually at least as hard and possibly quite a lot harder than implementing the compiler yourself to begin with It's better if we write proofs and let someone else write those proofs and so the reason that the parser combinators thing I Want to try to make less intimidating is because it really is less intimidating I mean, what's more intimidating write a specification for your protocol or writes Verified program that you can show is correct that transduces from one to the other Really? Um I'll just skip that slide since I think I'm running a little bit low on time So here again are the basic tenets of language theoretic security from the paper that I mentioned The the seven turrets of Babel the first one we discussed is shotgun parsing where continually You have this issue whereby? Your your state that is input is processed a little bit Modifies your program state and influences the things that are in the threat model and then we continue later to decide whether or not the input is malicious Don't do that Input language is more complex than deterministic context free This is one that I often like to explain by saying your input protocol should look no more complex and intimidating than JSON And shouldn't look across to other JSON documents to figure out whether the current one is valid This is a good way of explaining it But it leaves out one of the most important things which I was talking about in previous slides Which is that although you might well be able to enumerate all of your inputs and thereby make it less complex than Deterministic context free You shouldn't Because that model is not amenable to figuring out whether the bugs are there You can't meaningfully reason about it and the reason that the input language shouldn't be more complex than Deterministic context free is because you've exposed if it is more complex too much functionality potentially a complete Turing machine to your attacker and So if you instead of exposing Something that's high on the Chomsky hierarchy to your attacker. You just expose a lot of very low-level complexity We found that these things are exactly equivalent And we found that in in our experience designing this component even with a truth table that was only a few columns wide We still had immense trouble checking that our constant Implementation actually matched the thing that we were intending to do and actually a program even though Turing complete may well have been much easier to check Non-minimistic input handling is another one that I haven't really gone over here But this is effectively the same thing as shotgun parsing in many respects It's your input handling should have as few features as possible. This is the turn off XML entity expansion in sacks simple enough on if you the the science here is don't expose complexity to your attacker that doesn't need to exist and So if your input handling does anything other than just check the input put it into deserialized types and verify whether or not it's valid Then that's additional power given to the attacker that didn't necessarily need to be there Parcer differentials is another one This one simply explained is I have a line and both ends of that line disagree about its meaning This is when I cross-connected and one of the lines that detects whether a path is occupied To whether or not There's an incoming call in a line If both ends don't agree about what exactly the protocol means Within very well enumerated bounds for example if you have an X top 509 parser and it doesn't understand what a critical section is And so ignores it Your violation of the specification is likely to be material And so avoiding parser differentials is something that is best done by making sure that your specification is not incomplete and that people when implementing it Actually implement the full specification An incomplete specification is more or less inviting parser differentials because it leads people to guess What the meaning of particular special cases in the protocol are and that NTP bug that I mentioned earlier where a null key would Allow you to just assert authentication and update the time on fairly high stratum clocks low stratum clocks The problem there was that the specification didn't really say and in fact the reference code for NTP Implementations didn't show that this particular flow should not be allowed. And so it was allowed oops Overloaded fields is another one. That's really evil and we try to overload fields as little as possible When designing circuits one might find that fields often get overloaded Because you have a pin and like depending on the context that pin might be an input or an output You have to be very careful with this That's kind of playing with fire because if the circuit isn't in the state that you think that it is The pin doesn't have the signal on it that you think that it has and so thinking back to my circuit diagram from before We thought that the pins 1d through 3d on that latch Would have the correct signal on them for which line you should be on when the latch flipped over it turned out That that was not actually the case And and that's why it was possible to accidentally be put on to pass that you shouldn't have been put on effectively because that field was was a little bit overloaded and we didn't Like the pin could have been either find the next available path or find the path that has an incoming call on it for me that's next but We didn't well enough define By not putting the correct timing capacitors in When it was one state or when it was the other and so effectively that field had two meanings at once and that is a thing That you cannot have go on ah So this isn't the invent signaling part of the phone switch No, it does sound a little bit like invent signaling doesn't it because the phone network is one if you wanted to model it that way One gargantuan circuit um, so no It's it's almost invent signaling though This is all behind a logic boundary on the only input in it that depends on the line Electrically is whether or not the line is picked up But if you allow these signals to ingress for example They're a see or we're allowing people to charge the capacitor we can have emergent invent signaling and that is effectively what the bug is Permissive processing and valid input I think I went through more than enough you have to know that your inputs are what they say they are and If you intentionally be liberal in what you accept without knowing what it is that you're accepting You also have a proclivity to get bugs in this case Maybe I should have used an inductor to filter out all of the AC because I really didn't want that it causes glitches and so With that I believe I am out of time so please If you have any other questions for me, I'll be at the shady tell camp, but that's me on Twitter I'm also available by email eventually. Thank you very much And apparently I do have time for questions. So if anyone has any please fire away until I get cased off here. Hey Where would one get started how to do Langsec, um, so Yes, the seven turrets of Babel paper is specifically designed to be accessible to software engineers with no No background in the ways that academics prove whether or not something falls within the complexity classes that we wanted And so I would actually start there Yeah, the seven turrets of Babel or Langsec.org has a lot of material Some of the material is less accessible and more academic, but some of it is articles and things really unfortunately because Yeah, I'm still the best source of knowledge is there And the majority of the rest of it because it remains very much an academic field of study And if you happen to have access to academic papers on Langsec is a good keyword to search and Every year there's a workshop Attached to the Oakland conference. It was just a few months ago IEEE security and privacy workshops has a Langsec workshop that we present all of the new and latest developments in defining the terms in our field and anyone else Go ahead One way to look at Is Okay, I'll try and repeat that as much as I can on so the question was basically first pointing out The bug the timing attack that we have in here seems to derive from the difference between the fact that we're reasoning about the circuit in discrete time and yet It's really a constant time circuit And and the answer is yes, absolutely. That's where the bug comes from the additional was this Could be potentially solved by a clock although having the clock in the additional state adds additional complexity Which would also then have to be reasoned about This is also very valid. And so the question was Is there a conscious trade-off that we made between those two things? The answer is absolutely yes We found that for a circuit of that class the complexity that would have been introduced by a clock would have been Close enough to the complexity that would have been introduced by a microcontroller We would have had to make sure that the states had settled before the clock ticked over anyway We we chose to make it discrete by adding delay capacitors so that the latch would have time to settle and we would be operating within the data sheet and We did that wrong So we should have been reasoning about it in continuous time But we weren't and that is how the bug happened And yes, this does totally play into the things that I was just talking about if you're reasoning about the wrong Level of your computation model, then you won't find bugs that are there We found all of the bugs that were in the discrete computation model But we didn't reason about it in continuous time And so we didn't find any of the bugs in the continuous time model And that's really also the same thing as the a2 malicious hard analog malicious hardware Trojans They reasoned about the x86 processor at the VHDL level But they didn't really reason about it at the compiled VHDL level because you can't and that's really why that paper is So wonderful Yes, the answer is absolutely. That's a consideration Yeah, just reduce the number of things that you have to reason about the the question was is the thesis the idea that you should Use a parser combinator to generate your descriptions thereby going forward and up the abstraction model Yes, because you get to take advantage of the fact that all the things that are underneath it at the other computational levels are also verified