
34c3 preroll music
Herald Angel: Today we have our speaker

Filippo Valsorda. He is a cryptoengineer
and he is specialized in Go. He is some

kind of Go wizard so to say and used to
work at CloudFlare. He actually shows now

today in the talk how he made an attack to
exploit a bug in the implementation of the

elliptic curve P256 in Go, that's why he's
a Go wizard. The reason is actually due to

a misplaced bit on the assembler level,
just due to the curves implementation. The

result is that in certain cases you can
retrieve the private key in the elliptic

curve DiffieHellman encryption scheme.
Please welcome Filippo Valsorda to his

talk, give him a round of applause. Thank
you very much.

Filippo Valsorda: Thank you.
applause

Thanks. I love the term cryptogolfer. Tony
came up with it and I just want business

cards with that on it. Anyway, okay, I'm
Filippo. As you heard I worked on Internet

infrastructure, I work on Go, I work on
cryptography. But this is a collaboration

with Sean Devlin, you might know him as
one of the authors of the Cryptopals or

the matasano crypto challenges. A few
months ago earlier this year a bug.. a

CloudFlare engineer was scanning CT logs.
CT logs are big logs of TLS certificates

and he was checking the ECDSA signatures
on these certificates and one of the

signatures was not verifying. Which is
weird, because CT logs check the

certificates before adding them to the
log. It turned out that the bug was in the

code that CloudFlare was using to verify
the certificates. And more specifically it

was in the Go standard library. It was in the
implementation of the NIST P256 curve,

which is a popular, very hard to implement,
elliptic curve that is used for example

for ECDSA TLS certificates. This curve has
an assembly implementation in the Go

standard library, of course, to squeeze
every bit of performance out of it, and

it's specifically optimized for x8664
architecture. So that's where the bug was.

There was a carry propagation bug. It was
reported upstream and everyone agreed that

this was not obviously exploitable.
But Adam Langley also said that it would

be a cool paper though. And well I mean,
how do you pass on that? So Sean and I met

in Paris and spend a weekend and then some
eating Thai food and staring at this

assembly code to try to understand what is
it doing. And one month later we have a

CVE and two Go security releases. We found
a way to go from this single carry bit bug

to a full key recovery against protocols
that use elliptic curve DiffieHellman

within ephemeral static way. If this means
nothing to you, it's okay, I will try to

go over it. One of these protocols for
example is JWT. Jozy, or however you want

to call it  it has 15 different acronyms.
And it's often implemented in Go, so this

was exploitable against realworld
services. So, let's start by looking at

the code with the bug. Let's take it from
the beginning. This is the short assembly

function that does subtraction. When you
do elliptic curve math, you usually work

on a field, you work on math modulo some
prime p. So if it was subtraction you do a

minus b modulo p and this is what this
assembly snippet does. It sets a to a

minus b. Of course these are numbers way
too big to fit into a single register. So

how do you do math, when you can't fit
into a single register? You do multi

precision math. And the thing is, you all
know how to do multiprecision math, you

learned that in elementary school. When
you would write numbers in columns and you

do math with register size of 10, because
for every digit you would subtract two

digits and carry the minus one if it went
negative and then subtract with the carry

and then carry the minus one. That's
exactly what this is doing, but it's doing

it instead with digits with 64bit registers,
because this is a 64bit architecture

. So we look at the first
lines: the first lines is nothing else

than subtract, subtract, subtract with
carry. And then the carry is finally

stored in that mul0 accumulator. But then
what do we do if it goes negative?

We said that this is modulo p so we can't
just let it wrap around modulo 2^256,

because that's how wide, you know, 4
registers are. But since we're doing

arithmetic modulo a number, we can just
add that number and the result is the

same, right? Adding p modulo p is a noop
in the result. So that's what this code

does: It does a equal a minus b, it takes
a copy of the result and it adds p. And

then in constant time, it uses that final
carry to check, if it went negative or not

to decide in constant time, which one to
use. The one with b , so a minus b plus p

or a minus B  and that's subtraction.
Straightforward enough. Now the problem

with this code is that if you look closely
you can see something that might be weird

if you're not familiar with assembly. It
still trips me over. To use a condition

like these constant time conditionals
there. Which have to be constant time,

because you don't want to leak timings
based on the size of number. You have to

first operate on mul0, so that you set the
flags, the zero flag. So normally what you

do is either add 0 and 1 to your mul0
to check if to set the flags. But that's

not that's not an add, that's an add with
carry. It means that it's adding zero to

mul0 and the carry bit from this addition
here, which has nothing to do with it,

it's not supposed to be there. Like this
is adding p, but it doesn't matter, if it

overflows, because if it does, it wasn't
going to be picked here anyway. So it's

adding another bit into the operation that
wasn't supposed to be there. So it's

flipping the result, but then also the
conditions here are flipped, so

essentially it evens itself out. Except:
when that carry bit happens not to be set,

because the number a minus b is small
enough, that if you add P, you don't wrap

around. That happens once every 2^32
times, which is why it's so rare that

nobody had noticed so far.
So the code went in and nobody noticed for

a long time, until CloudFlare first
started scanning CT logs and had this

weird one signature that wasn't verifying
and they were lucky enough to actually

have it in the logs because you know if it
happens transiently you might just think..

I don't know, the connection broke, right?
So this is what we call a carry

propagation bug. Carry propagation is the
activity of making sure that you carry the

1. And here it's a bit weird, we didn't
forget to carry it, but we carried a bit

that we weren't supposed to carry into a
result. Most of the times that goes well,

sometimes that breaks. When that breaks:
wrong result! Wrong result, wrong point

computation and wrong point computation,
so what? Like how does forgetting to carry

the one lead to a full key recovery? This
is not one of those bugs, where like

buffer overflow you know what you're
trying to do even if you might have to

jump through so many hoops, because there
might be all these protections, you know

what your capabilities are, you can
overwrite some memory. Here instead it's

not clear what your capability is. So
today I'm going to try to explain that.

How we turn this very rare failed
computation into a complete key recovery

attack. But first I apologize that I have
to give a elliptic curve cryptography

crash course here at CCC. So we've seen
the field is nothing else than operations

modulo p, then there are the points: the
points are x and y, the coordinates. They

fit an equation, which we don't care
about, they just fit an equation. They are

integers, so we can work with them, and we
can use them to build a group. A group is

one of the core structures in modern
cryptography. To build a group we need a

zero point, a generator point, and an
addition over the group, over the

points. So we define an addition,

again, we don't care about how addition
works. It's just: you take two points, you

add them together, you get a result.
It has all the properties that you

remember from elementary school addition:
it's commutative, it's associative. And

then you have multiplication: You don't
actually define how to multiply a point,

but if I tell you that you have an
addition operation and I want five times

this point, what do you do? You take the
point and you add the point and you add

the point and you add the point,... so
this is called scalar multiplication:

doing multiplication by adding repeatedly
a certain point.

So now we have a group, which is given by
multiplying the point, a generator point, a

certain number of times and we have a
multiplication operation. So how do we

build cryptography out of it? This is all
awfully abstract so far? So we build

elliptic curve DiffieHellman. If you're
familiar with normal DiffieHellman, you

will see something snap at some point. The
private key is a giant number, this is

important. The private key in ECDH is just
a random giant 256 bit number. And then we

have a public key. A public key is that
giant number multiplied, the scalar

multiplication we just talked about, by a
generator point. If you know normal

DiffieHellman, this is like doing g to
the a. If you don't, it's okay, this is

just multiplying private key by a point.
And then, when I have my private key and

my public key, you send me your public
key. We need to agree on a shared secret,

how we do that, is that I take your public
key, which is a point. I take this and I

multiply it by my private key, here, so
again: it's my giant number private key

multiplied by your point. That comes
together: The two results are the same,

because if we do my private key times your
private key times g it's the same as your

private key times my private key times g,
because that's commutative. And we land on

a shared secret. And that's all we need to
know about elliptic curve cartography to

exploit this. However, there's one thing
that I glossed over: It's easy to multiply

by five, you add five times.
But if I'm asking you to multiply by a 256

bit number, you can't add 2 to the 256
times a point, right? So what do we do

there? Remember that here, what we're
trying to do is the multiplication: The

private key times the public key, the
point. We do something called double and

add: We take our private key and we string
it out like bits. We start from the most

significant bit. This is little endian,
because I have gotten this slide wrong the

first time. But, you know, you just claim
that you meant it to be the opposite

endianness. Anyway, that's the most
significant bit, the one that is two to

the 256, no, two to the 255. If you flip
it, you're adding or removing two to the

255. So you start with 0, that's zed, 0,
nothing, and you check the first bit, the

most important bit: Is that set, yes or
no? Yes, so you add the point Q, which is

the point we're trying to multiply by this
giant e. So we add the point and then we

move down one by doubling. Can you imagine
how we double something? Remember we only

have addition. We add it to itself, of
course. So we use addition to double the

point. And you might see, where we're
going with this. We double every time we

slide down one bit.
By the time we arrive at the end, how many

times did we double that first point that
we added, because the first bit was one?

255 times! That bit was worth 2 to the
255, so at the end that will have the

value it was supposed to have. And we keep
going, we check if this bit is 1: Is it 1?

No. So we do nothing, we double again to
move down one. We check if this bit is

one. This bit is 1: so we add the point.
So we did add the point, double, double,

add the point, double, moving down one and
we keep going like this. We keep going

like this, until we reach the least
significant bit. At the least significant

bit, if it's one, we add the point, if
it's not, we don't add the point. And at

the end we have the correct result and the
result comes from this sequence of

operations which at most are twice 255
operations, which is something that we can

do concretely. Now why did I explain this
very specific algorithm to you. Because to

understand this attack, you have to
recognize that each key, so each string of

bits here, converts into a very specific
sequence of operations. Okay, if you

change one bit, there will be an one more
addition one less addition and each key

has a very specific sequence. In this case
it's add, double, double, add, double,

add, double, double, and it keeps going.
So back to our bug. If you spaced out,

because we took a lot of crypto, I saw
you! But the two things you should take

away are: There's a giant number, it's the
private key, we want to multiply the giant

number by a point and we do that by doing
additions and doubles in an order that is

specified by the bits of the giant number.
That's what you need to have clear, the

only thing. So let's go back to see how we
use that to turn our very small carry bug

into a complete key recovery attack. First
thing we do is we bubble it up. That

function that breaks is called P256
subinternal. That's the function I showed

you earlier. P256 subinternal is used by P256
point add, which is what we spoke about

adding two points, the only important
operation. And adding two points, we've

seen, we use when we're multiplying, when
we're doing that scalar multiplication,

which is d times q, d times the point. And
how is scalar mult used? Here we finally

surfaced to a level that if you work with
cryptographic protocols you might start

recognizing pieces of. Scalar
multiplication is the operation that the

peer does in elliptic curve Diffie
Hellman. There's a scalar, which is the

secret, which is the private key. There's
a point, which is the public key of the

other person, which might be the attacker.
So the scalar multiplication here,

speaking in InfoSec terms, has an attacker
supplied point and a secret scalar and the

result, the shared secret, is the session
key. For example: TLS, when you open a

connection with TLS and you're using
elliptic curve DiffieHellman, then you

will do this dance to agree on a session
key. If the session key is correct, the

connection will open and you will be able
to, I don't know, get send HTTP request.

If the bug is hit and the result is wrong,
so the result bubbles up into a wrong

shared secret, the session key is wrong.
And the session key is wrong you notice,

how do you notice? The connection breaks.
So this is what in cryptography we call an

oracle.
You have an oracle that you can call and

send a point, because that's your public
key, you're the attacker, and you send

that point and it gets multiplied by the
private key. And it gives you one bit of

information, did the bug trigger or did it
not? Most of the times it won't, because

remember, this is an extremely rare bug.
So you have an oracle that tells you: Bug

happen, bug didn't happen, based on the
point you sent. And let's assume that the

key stays the same, we'll talk about that.
Can you imagine how we use that to start

learning things about the key? Well, let's
say, that we can magically conjure a point

that in that sequence of operation, that's
why I told you the sequence of operation

was important, the bug happens very
specifically at that addition and we find

another point where the bug happens very
specifically at that double. If we know

already these bits of the key, and we
aren't sure about this one, what can we do

with these two points? We send them both,
one of them will break the TLS connection,

the other one will succeed. That means
that the one that broke triggered the bug,

the one that didn't break, didn't trigger
the bug. And we know which one corresponds

to which key. Because we magically made
special points that only break very

precisely at that point of the
computation. Okay, so we learned a bit of

the key. In cryptography as soon as you
learn one bit of the key, there's probably

a way all the way down. So we build what's
called an adaptive attack. Let's say we

have these bits, but we want to learn
these, too. We compute two points, one

that breaks, when the addition happens
exactly at that point in the double and

add a procedure, and one that triggers
only when the add doesn't happen at the

specific point in the double and add
sequence. We send them both, only one of

them breaks the TLS connection, well, then
we know a bit! We go back to our magic

point generator and we generate two new
points.

This time we don't look for things that
break here, we look for things that break

here. We make two points, we send them
both. One of them breaks the connection,

the other doesn't break the connection. We
learned one more bit of the key. We go

back, we make two points, we send them
both: One breaks the connection, one

doesn't. We keep going like that, once for
each bit. Every time we go back and we

adapt to what we learned so far. That's
why it's called an adaptive attack, we

can't precompute all these points. We
have to come up with them while we learn

the key. And the beautiful thing about
adaptive attacks is that they look exactly

like Hollywood, it's beautiful! Because
you see them flipping and going through

values, getting it right and moving to the
next one, which you all told it was fake,

it was not! Everything else is fake. Okay,
so, this attack we came up with that, we

told we had something extremely novel, we
went to the literature and everyone that

had picked an academic career in the
audience knows exactly what happened: We

found a paper that was doing exactly this.
However, you know, it was a little

different. It was still P256 and it was
still ECDH and, hmm.. okay it's really

similar. But it's an attack that depends a
lot on the implementation details, how you

pull it off. You can't suddenly just
repurpose the code, but the idea so far:

an adaptive attack where you send two
points and you check which one breaks is

the same as a BBB paper from a few years
ago when it worked against open SSL

instead. Instead of against this bug in
the Go standard library. So instead from

now on we're going to talk about how
exactly we implemented this against Go,

because this changes a lot based on the
implementation details of the library

you're working with. So this was the
general idea of how the attack works. All

that: You find points that break at the
right time, you send them both, and that

way you learn a bit of the key,
except I lied to you.

I lied to you, because I lied to you on a
lot of things: The first one is that Go

doesn't do double and add one bit at a
time, it does it five bits at a time! It's

called Booth multiplication, it took us
forever to figure it out. It's an 1980s

paper. Instead of adding one point or zero
points and then doubling, it adds between

minus 16 and plus 16 points and then
doubles five times moving down. It just

does it in blocks of five. So it splits up
the key and then looks at each block of

bits, picks a value from a precomputed
table, which is just all the values from

one times the point to 16 times the point
and in the loop it doubles five times,

because it moved five bits down. And then
it chooses, which point between zero and

16 to use from the table and it adds that
to the rolling result, instead of adding 1

or 0. There's also a bit of constant time
arithmetic there, because the other thing

I lied to you on is that there's no such
thing as at 0 point. It's an imaginary

point that we add to make the math work,
but when you try to tell the code to

actually add the zero point it's like
asking you to divide by 0. It just won't

do it! But you know how you add 0, right?
You do nothing! So there's some constant

time code here that looks at it and if
it's 0, it does nothing. If it's not 0, it

adds the value.
So the first thing we had to do to adapt

to this format is that we worked in limbs.
When you hear me say limb, it just means

that we will look at each five bit block
on its own as it is zero to sixteen and a

sign value. That's much easier, because
it's actually kind of weird how the five

bits are extracted and I don't want to
bore you with it. So let's just consider

that we look at each group of five bits
converted into a value from zero to

sixteen and a one and a sign or plus minus
sixteen. So when you hear me talk about

limbs you just know that it means the five
bit value from the key. This is still the

giant d key that's the multiplier. So how
does the attack change? Not that much!

Instead of attacking one bit at a time,
you know two points  one that breaks for

zero, one that breaks for one  we attack
one limb at a time, one that breaks for

one, one that breaks for two, one that
breaks for three, sixteen, minus one,

minus two, minus 16. So, to recover five
bits of the key, we'll need on average

half the space: seventeen points, which is
a little less efficient than the bitby

bit one, because that would be five points
for five bits. So, however, how the attack

triggers is the same: We're looking for a
bug that happens in the five doubles at

the very right time or that happens at the
addition at the very right time. Now

that's still the high level of how we're
going to do it, but in practice I didn't

tell you how we're going to magically
generate these magic points that break

just at the right time. And I didn't tell
you because it's fuzzing. There is there's

no specific way to generate them
algebraically, so instead we just hooked

the assembly code with something that
would just set a boolean, you know, true,

false, if the conditions for the bug are
met. And then we run through a lot of

points. And if for each point we run it
through the limbs we already know,

remember this is an adaptive attack, so
we want to learn the next limb.

We learned a few limbs, we want to learn
the next one. We run through the ones we

know and then we try all the possible
values for ones that we don't know. If one

of them and only one of them breaks,
that's a useful point. Because if we send

that point and it breaks, we know exactly
what the value of the next limb is. Okay,

so this is going even more lowlevel. If
you're interested in optimizations, we had

to run through a lot of candidate points
and for each point we needed to know the d

value. Because we can find a magic point,
but if we don't know the private key, we

don't know if the entire protocol works
and our oracle doesn't work anymore. So to

work with that instead of multiplying a
new random private key every time we just

add that one to the private key and added
the point to the public key, this is just

an optimization. We called into the
various assembly of the standard library.

Don't do this, but it's beautiful. You can
go call all the unexported functions in

the standard library. I will never approve
it on code review, but I love it. And then

there's some postprocessing to make sure
that the bug is actually there, because

sometimes there are false positives. So we
just run it through the broken code, the

fixed code, is the result the same?
Well, false positive, is it

different, good. Okay, so we have a way to
move through the attack. The only thing we

don't have yet is: How we figure out the
first limb. The first one, the most

important, the most significant one, when
we still don't know anything about the

key. The problem with this one is: It's
like this, so let's pick three, it's an

example okay, let's pretend that the limb
is three. So we do our usual thing, we do

our fuzzing and we find something that
breaks at the fifth doubling and we send

it and it breaks. It means that the first
limb is three, right? Wrong. Sadly, it

might mean that the limb is also 6 or 12.
Because how six is selected for example is

that three is taken, it's doubled and
saved in the precomputation table as six,

then it's taken out of the table, doubled
five times, but what happens after you

double six four times? What's the value?
The exact same as doubling three five

times, and the sequence is the exact same!
So we don't know, which one it is, because

we only know that that's the sequence but
that doesn't tell us anything. And the

same happens for 12, 12 is nothing else
than 3 double double. So if we double it

five times, at the third double it breaks
and we only know if it breaks or not. So

we can't move, so instead what we do is
that we find three points: one that breaks

after doubling three five times, one that
breaks doubling it six times, and one that

breaks after doubling it seven times. We
send them all and we look at which ones

break. Only this one? It's a three! The
first and the second but not the third

point? It's a six! All three? It's a 12!
This took me forever to wrap my head

around, this is like pure shaman insight.
Okay now I can feel that you're getting

bit tired, this is intensive, it's a lot
of math, so let's go for a change of pace

and let's talk about kangaroos instead.
I'm gonna tell you something that I

learned from a cryptographic paper, I
swear. And it's about how kangaroos jump.

Apparently kangaroos jump depending on how
the terrain on which they are jumping from

is. Depending on that terrain, if you put
two kangaroos on the exact same spot, they

will jump the same distance and
approximately the same direction. I don't

know, if this is true, but Pollard said so
in a paper and I am NOT arguing with

Pollard. So now, why is this useful? Well,
this makes for an extremely cool way to

catch kangaroos! I mean, did you expect
some math or crypto? I know, we're

catching kangaroos here! So you take a
kangaroo that you have on hand, because

you all have a kangaroo on hand. And you
put a GPS tracker on it and you let it

loose. This kangaroo jumps and it roams it
enjoys a brief stint of freedom and it

runs and at some point you go pick it up
and because you know where it is and you

place a trap there exactly where you find
it. What happens to the kangaroo that is

just passing by? If it steps at any point
on one of the points, where the other

kangaroo jumped from there on it will
follow the same path. Because each jump

depends on the ground. So this way if the
wild kangaroo lands on any of the prints

of the previous kangaroo you're catching
it because eventually it will end up in

the trap. Okay, this had nothing to do
with the talk, I just wanted to share

this!
Now, okay, so here is how this converts to

crypto. We can make elliptic curve points
jump like kangaroos, we just have to make

the jump deterministic based on the input,
based on the starting point. For example

we can take a hash, any hash, you design
the hash. Apparently that's popular in

cryptocurrencies to design your own hash
..

laughter
And you hash a point to another point and

when you want to do a jump you take the
point and you add it to its own hash, so

Q_N+1 depends only on QN, and this is just
like the kangaroo jump. How do you use

this for what we're doing? We want to
recover d, right? We want to recover the

multiplier, the discrete log it's often
called, of the public key. How we work

with this is that we take a tame kangaroo,
a point that we know the d off, and we

make it jump a lot. It keeps jumping and
we remember what the value is at the end.

We take that value at the end and we save
it. No need to keep all the points in

between, so we don't need some giant
memory construction and then we take the

point that we don't know the d of and we
make it jump a lot. What happens is that

it has much higher probability to
intersect one of the various prints of the

previous point. When it does, it will
eventually end up in our trap, it will end

up having the same value as the one we
calculated earlier. When that happens, we

know how far the wild point traveled,
because we were the ones making it jump.

So we can just walk backwards to the
starting point. It turns out that this is

extremely efficient to find the d value
when you know the interval of the d value.

The intuition there is that if the
kangaroo starts in a small area: You know

when it's been too much time that it
probably travelled past your trap. So you

have to rewind time, which in crypto you
can, and try again, because it went past

your trap. So the smaller the interval,
the more precise you can be, the more

efficient attack. This is called the
Pollardkangaroo attack. It's described in

original paper from the 1980s, it was
described on DiffieHellman first, but it

works on any group, so it works on
elliptic curves. And the elliptic curve

version is then improved by a few papers
later and there is a chapter in the

elliptic and hyperelliptic handbook
that describes it.

So we have it all, we have how to start,
we have how to run through the attack and

we have a how to end. So now let's get
concrete, let's pick a target, as I said

this attack works against JWT. JWT made
... a lot of questionable choices. One of

them is to include as one of the public
key algorithms elliptic curve Diffie

Hellman but not the version of Diffie
Hellman you and I are familiar with. The

one where we both generate a new private
key every time, which makes this attack

impossible. Remember that this is an
adaptive attack. We need to query the

orcale for the same private key over and
over and over again. Instead there's a

variant called ephemeral static Diffie
Hellman, where one of the two sides is

always the same. This is sometimes done as
an optimization, don't do that. OpenSSL

was doing that and it stopped doing that
after a bunch of attacks came from that.

So in TLS that usually doesn't happen and
the Go TLS stack thankfully never did

that, so the attack doesn't work against
TLS. But JWT is encoded it straight into

the standard, because if your public key
is fixed, so is your private key, always

the same. So if we have a service that
accepts things encrypted with a ECDHES

algorithm, we can use this attack. For
example against the popular implementation

GoJose, it's not GoJose's fault and Go
1.8.1.1, the latest vulnerable version,

and we can just check if the service can
decrypt what we're sending it. It can,

because it throws HTTP error when it
can't, because of different timings. This

changes in any case, but what you need is
the Oracle that tells you: did it work did

it not work? Did the bug trigger, so are
you right about your prediction of the

limb or are you not?
Then of course we need to do a lot of

work. If you don't have the resources of a
big corporation of where to spin up

things, you can just use EC2 spot
instances. How we architected that, is

that there will be a small piece of code
that do nothing intensive on your laptop

or anything. It would accept HTTP requests
from the workers. The beautiful thing

about this infrastructure is that you can
horizontally scale the workers, spin up as

many as you want on node.js platforms,
because the only thing that they need to

be able to do: they don't need to have
ports open, you don't have to worry about

NAT, you can even run it on your botnet,
because the only thing they have to do is

connect back and ask for work and then
after 30 cycles or when they find the

point, connect back and say I found
something, I didn't find anything, give me

some more work and send the result. This
was also useful, because if you get the

worker code wrong or if you want to change
the deployment, you can just redeploy the

workers without losing state on the
dispatcher. Because the dispatcher just

keeps running and it will just wait for
workers to come and ask. Specifically we

built this just with the small script that
you can start an EC2 machine with, because

we didn't even need to make a custom
image. So a few figures, a few numbers.

Each Key has 52 limbs, it will take a bit
less than that, because we have kangaroos.

But let's say approximately 52. Each limb
is 16 points on average. It would be 17,

but two of the points are faster to find,
so let's say 16. Each point takes

approximately 2 to the 26 candidate
points, before we find one that triggers

the bug at the right point. Since we need
16 candidate points and each takes 2 to

the 26 candidate points, that takes
approximately 85 CPU hours. That's like

one CPU running for an hour, 1 core. Turns
out that you can get 85 CPU hours from

spot instances for about a dollar and a
quarter, which makes the total cost of the

attack something like 60 bucks.
Which was a relief, because I had done the

math wrong first and the it came out as at
1000 and I run the demo tonight and I

didn't check the bill, yeah.. Anyway, it's
cheap. Now, I am not brave enough to run

the attack live, because, yes, it's a nice
infrastructure, but no, I don't trust it

that much. But also, because it takes a
while. If you don't want to spin up

infinite number of EC2 machines, you have
to accept that it would take about, I

think, our attack run in 12 hours. So
we're gonna look at a spedup version of a

onehour video in the next 45 minutes, you
have time, right?

laughter
No, it's a couple of minutes. So this is

the UI. It shouldn't be too confusing and
if anyone works at Hollywood and wants to

license it, we can talk. What you're
seeing is that the red values are the ones

that we found a point for from the workers
and we submitted. And when we submitted

that it resulted not to be the right limb
and the green ones are the ones that

instead broke, so they're the right limb.
Remember that here the target is a JWT

receiving application. And then you can
see the key slowly flipping from the

bottom and it's exactly like Hollywood, I
love that. So you can see the limbs

filling up as we find them and that
approximately there's 30 bits, so 2 to the

30 rounds, so 30 candidates work for each
round for each limb that we find. It

obviously depends on luck and yes the the
thing will probably keep running for a

little while, but this is already at limb
nine, it has to get to 52 and you don't

have that patience. So this was the attack
the code will be opensource soon. Leave

the limbs you lost, they belong to us now.
And: any questions?

applause
Herald: Valsorda, thank you very much for

a lovely talk and the kangaroos. So, we
have a question from the signal angel, go

ahead, please.
Signal Angel: Actually the internet wants

to know: Did you compare this bug to
implementation in other libraries.

Filippo: So I guess that means, if I
looked for similar bugs in other

implementations. We did not, because each
implementation is a bit different. Hanno

works on a lot of fuzzing of bigint
implementations and at one point he asked

me, like on Twitter just today, if I tried
fuzzing the Go implementation for example.

And sadly this is constant time code that
is specific to P256, so the answer is,

there's a lot of them and the bug can be
small and anywhere. It's not like you will

be looking for another bug in P256
subtraction, it can be anywhere in the

underlying math and we can turn that into
the same attack. So, no, we didn't look

for this specific one, but I think that
four CVEs in 2017 on open SSL have

descriptions that are very similar, but
they're about finite field DiffieHellman,

like normal DH. If you look for something
that says about it's barely doable,

because all the computation can be done
offline. That's this kind of attacks on

open SSL. Next?
Herald: Are there any other questions from

the Signal Angel? So please line up at the
microphone. Microphone one please.

Mic1: So why can't you determine the
points algebraically?

Filippo: Laziness? No, so it's entirely
assembly and there's a lot of points where

the value is then thrown out or it might
get corrected by .. it's essentially we

didn't see a clear path to this, and it's
$65 on ec2, so it doesn't really change

the feasibility to just fuzz them, so we
just went for the fastest path to the

entrance.
Herald: Are there any other questions?

Filippo: No one is asking about kangaroos,
people, I mean..

Herald: Yes, ask about kangaroos, lovely.
have you been to Australia?

Filippo: I haven't.
Herald: Okay, you have to.

Filippo: Yep, definitely.
Herald: I think, there aren't any other

questions. So Filippo Valsorda, big round
of applause for you. Thank you!

applause

34c3 outro

subtitles created by c3subtitles.de
in the year 2019. Join, and help us!