 Hello. I'm Nikos. I'm a cryptography researcher at FireBlocks. This is Zorin. He's a security researcher at FireBlocks. And we're here to talk about hacking crypto wallets. So what is a crypto wallet? So let's say you want to spend Bitcoin. You need a wallet. That wallet holds a key. That key allows you to sign transactions. Those transactions you can broadcast to the blockchain and send crypto to your favorite people. There's a problem with the setup because there is a single point of failure. The key is sitting in one place ready to be stolen. How can you minimize that risk? You can use multi-party computation, also known as MPC. So what is MPC? So at a very high level, the idea is that instead of having one key in one place, the key is split into shares. And those shares reside with different parties. Actually MPC promises much more than what the picture suggests because the key is never assembled in one place. The parties calculate their shares and the public key via an interactive protocol. And then every time they want to sign a transaction, they run a different interactive protocol for calculating the signature. So small aside, MPC is much bigger than the use case I just described, which is threshold signatures. It is the crown jewel of modern cryptography. Basically anything you can do with a trusted centralized party, you can do trustlessly with MPC. There are numerous examples. I'm not going to go into them. But what I will say is that commercial adoption has been a bit slow. MPC has been around since the 80s. And the most significant use case before blockchain was the famous Danish sugar beet auction of 2008. And since the advent of blockchain, MPC is now protecting billions of dollars in cryptocurrency wallets. But we're not here to talk about MPC in itself. We're here to hack crypto wallets that are based on MPC. So what would that look like? One thing that could happen is in the now of service, let's say I'm able to freeze your wallet so you can't spend your crypto. That's a problem. Something more severe would be signature forgery. Let's say Nikos and I, we have an MPC wallet. So every time we want to sign, we sign together. So I tell him, let's sign this $10 transaction to your favorite charity. He signs it. But because I took advantage of some flaw in the protocol, I actually made him sign a totally different transaction without him knowing. That's bad. But what's even worse would be private key exfiltration. Whereas by him signing that transaction, he leaked his key share to me and now I can sign whatever I want whenever I want without his consent. Today's talk will be focused on this. We're actually going to show you three different attacks that all end with private key exfiltration. When we'll be talking about attacking crypto wallets, MPC crypto wallets, I want you to have this threat model in mind. So these are a bunch of people participating with an MPC wallet together. So each time they want to sign, they need to collaborate. Now one of those participants, namely Alice, will be acting maliciously. Now either she woke up one day, decided to be evil, or maybe she was compromised by an attacker and now is using her identity to attack the other people. So whatever her motivation might be, she wakes up in the morning, her only goal would be to exfiltrate the other counterparty's key shares. And she will do that by signing normal looking transactions with them. So the research findings that brought us here today were as followed. We have discovered four novel attacks, including three zero days. These findings have affected over 16 different vendors and or open source libraries. We'll be releasing at the last slide four fully working proof of concept exploits for these attacks we'll be showing you. We actually use the very same proof of concept exploits against two vendor production environments and got the private keys. And the last thing I want to stress is most of our attacks are not implementation specific. We'll be attacking MPC at the protocol level. So the affected parties were numerous. Some of the biggest crypto wallets were affected and a number of crypto custodians were affected. The most popular consumer MPC wallet was affected as well as some of the most popular open source MPC libraries. The attacks will be covering today. We'll start with attacking the most popular two party signing protocol. It's called lindl 17. Carrying out the attack will be somewhat high interactivity required. Then we'll go to the second attack. We'll attack the most popular multi-party signing protocols, the GG18, GG20 family of protocols. And last but not least, we'll attack what we call a do-it-yourself protocol because it doesn't really closely adhere to any academic paper. Okay, so before we have a look at the exploits, we need a few items of background material. The first item in the background is the math. Thankfully, no math. I mean, no advanced math. The only thing, we're not going to be talking about elliptic curves or abstract groups. The only thing you need to know for today is the modulo operator. What is the modulo operator? Well, very simply, x modulo n is the remainder of x divided by n. For example, 5 modulo 5 is 0 because 5 is divisible by itself. And 6 modulo 5 is 1 because 6 is greater than 5 and there's overflow and yada, yada, yada. Great. Second item in the background is homomorphic encryption. This is a cool thing. So what is it? It is a special kind of encryption that allows computation on encrypted data. Without decrypting the ciphertext. That's very important to note. So, for example, let's say Alice sends an encryption of the number 42 to Bob. So here and throughout the talk, we will be using color coding. Red for values that Alice knows, blue for values that only Bob knows. So Bob receives this thing and he can operate on it. For example, he can multiply by the number 2 and he can add the number 100. He does this obliviously without knowing what's happening inside the ciphertext. Alice, after receiving the resulting ciphertext, can decrypt it and learn the result without ever knowing what Bob exactly did to the ciphertext. So there are many kinds of homomorphic encryption. I'm not going to dive into them today, but we will be looking at the most vanilla kind, which is parametrized by a public key n. This is a number and it's the product of two big primes. This detail will become relevant later in the talk. So the last item in the background is the ECDSA signature scheme. This is the most important signature scheme in the blockchain space. It is used by Bitcoin, it is used by Ethereum, among other blockchains. And the protocols will be looking at implement this scheme. So how do you generate an ECDSA signature? It goes something like this. First, you sample a random number K. This is a secret and it is chosen afresh with each signature. It is also called the ephemeral key. Then to calculate the signature, you simply compute a function of the message to be signed, the ephemeral key, the private key, as well as an ECDSA constant that everybody knows that we denote by L. So that's for one party. What about multi-party? What about two parties? Well, the idea that we've already alluded to at the beginning of the talk is that instead of having the secret material residing in one place, the secret material will be split between the parties. Okay, so having covered the background, now we can move on to our first exploit. We will show you how to compromise the LINDEL 17 protocol, implementations of the LINDEL 17 protocol. And in order to explain how to compromise it, I need to tell you what the protocol is. So it goes something like this. So first, there's a key generation phase where the parties choose random key shares, so that would be X1 for Alice and X2 for Bob. The next step in the key generation is for Bob to encrypt his share under a homomorphic scheme that he controls, and he sends that encryption to Alice. So he sends an encryption of X2 to Alice. I will remind you that only Bob can decrypt this thing, but Alice can operate on it. And this is going to be important for signing. So before we move on to signing, we need to address the elephant in the room. This is a cryptography talk, so there's going to be formulas involved, but nothing bad is going to happen, or it will take care of you. Okay, so let's have a look at signing. Signing goes something like this. So Alice uses the ciphertext she receives from Bob during the key generation to send an encrypted partial signature to Bob. The encrypted partial signature looks something like this, and the only reason I am displaying this formula is to show you that we have almost all of the ingredients for the ECDSA signature scheme. We have the X's, we have the message, we have K1, we have the ECDSA constant L. The only thing that's missing is K2. So how does Bob finalize this thing? So he simply decrypts this value, multiplies by his contribution to the ephemeral key, and reduces everything modulo L. At this point, he checks whether this thing is a valid signature. Why does he need to do that? Well, he does this because, you know, maybe Alice fell asleep while during the signing process, and she forgot to multiply by her K1. At this point, Bob will fail to verify that the signature is valid, and Alice will be notified of this, either because Bob tells her, or because the signature never made it to the blockchain. So what happens in this case? Well, to answer this question, we consult with the paper the protocol is based on. And the paper has a very interesting assumption, not assumption, instruction. It says any abort will result in, will imply no later execution, which means that if Bob detects an invalid signature, the wallet must be locked. Putting our adversarial hats on, we see this, and we think, well, maybe we can leverage this into a denial of service attack. Maybe we can act as Alice and send some garbage nonsense partial signature to Bob, and now the wallet is supposed to be locked, right? So we went on to code this. We found some open source implementation of the Limbendil 17, and we tried to do this. To our surprise, nothing happened. Basically, we tried again and again, and the wallet was never locked. So we're kind of devastated by our failure to mount some denial of service attack, and we went back to the drawing board. We're trying to understand, wait, if this assumption, this instruction is part of the paper, why is it there in the first place? So the paper is actually very clear about why it's important. It says the only problem is that Alice may send an incorrect partial signature to Bob, in which case the mere fact that Bob aborts or not can leak a single bit about Bob's private share of the key. Now, taking this intuition, and like taking it to its logical conclusion, we might think of an hypothetical attack that looks something like this. First, Alice would craft a malicious signature that would fail to verify depending on a single bit of Bob's key share, and then she will see if it verifies or not. Then she can do this again, and again, and again, every time by seeing if the signature verifies successfully or not, knowing if that specific bit is one or zero, 256 signatures later, bam, she has the entire key share of Bob, she can combine it with her own and sign away without his consent. So this is very easy, you know, to do in PowerPoint with pictures, but to actually get the math to do this, you need a cryptographer like Nicholas. So that's where we stopped relying on the paper and we had to think for ourselves. And what we did is we had a long hard look at the partial signature that Alice sends to Bob. And we tried to mess around with it. We tried to like, you know, tinker with the X, we tried to tinker with a message, nothing happened, we tried to tinker with the K, again, nothing happened. But then the crypto gods smiled on us, because we had the following breakthrough, we realized that if Alice switches the modulus, so instead of using the L parameter of ECDSA, she uses the n public key of the homomorphic encryption scheme, these two values are equal after Bob decrypts, if and only if X2 modulo K1 is equal to zero. So I will spare you the mathematical mumbo jumbo and I will give it to you in a picture. Basically, when Alice switches the modulus, the following thing happens. If the signature is valid, X2 is divisible by K1. And I will remind you that K1 is a value that Alice controls and she knows. If the signature is invalid, then X2 mod K1 is not equal to zero. And this is this is this is very good for the attacker, because this already gives a way to learn the least significant bit. Why? Because Alice can simply set K1 equals two. In this case, she mounts the attack, she switches the modulus, and she obtains leakage X2 modulo 2 equals zero or not. And this is exactly Bob's least significant bit. Okay, so that's one bit. But what about the rest of them? So for the next bit, you can use the next power of two, namely four. When you mount the attack, you obtain leakage whether four is whether X2 is divisible by four. So this is great leakage. If X2 is already divisible by two. Because if not, then the leakage that you actually want is X2 minus one is divisible by four. And to obtain this leakage, well, what you need to do is you need to do some math. You need to massage the algebra, you need to add an offset to the partial signature in order an offset that depends on whatever you already know of Bob's key. And this will give you the leakage that you actually want. So to summarize, in order to exfiltrate the ith bit, you set K to be the ith power of two, you offset the partial, Alice's partial signature by a value that depends on whatever you already know about Bob's key. And in the end, depending on whether the signature fails or not, you obtain leakage the ith bit of Bob's key share. We didn't leave it at this and we want to see if it would actually work. So we went out, we found the most popular open source implementation of the Lindel 17 protocol. It was published by Zango. And we tried to see if we could make it work. So this is how the POC looks when it's run. As you can see on the right hand side of the screen, every signature leaks us a single bit of Bob's private key share, depending whether the signature failed or not, we know whether it's one or zero. We only need to do this 256 times. Now it may sound a lot, but it only takes a couple of minutes. And bit by bit, we leak the entire key. Now we can combine it with our share of the key and we have the entire private share. We can print it in hexadecimal format, load it to MetaMask or your favorite crypto wallet and you're done. So we've disclosed this to all the affected vendors and they've since mitigated it. How did they do that? Well, they simply followed the paper's specification. So like after you see that there was a failed signature, just don't sign again. Now this works, right? It mitigates the attack. But there are also other ways that you could, you might want to do it where you don't get a possibility of a denial of service attack. And to explain a possible way of doing that, we'll need to introduce a new cryptographic primitive. It's called a zero knowledge proof. What is a zero knowledge proof? Basically, it's a proof that yields the validity of a statement and nothing else. So to illustrate with an example, let's say Alice encrypts some number x along with a zero knowledge proof that that number is within some range, for example, one to 42. Now Bob can receive this and verify the zero knowledge proof and be certain that what he has is indeed an encryption of a number between one and 42, but he knows nothing else, only what the zero knowledge proof wanted him to know. So taking the intuition of this thing, we could maybe add a zero knowledge proof to the protocol in order to make sure that Alice cannot deviate from calculating the partial signature in the way the protocol says she needs to do. Well, congratulations, you've learned how to compromise implementations of the most popular two-party signing protocol, also how to mitigate it. And we're on to our second attack of the day, which is attacking the most popular multi-party signing protocol, meaning it can be two parties, three parties, or a hundred even. So this has affected many vendors and open source projects. So when we talked about the Lindell 17 protocol, we actually explain how it works, like start to finish. But the Digi protocols, the thing with them is they're very complicated. They actually, to sign, they require between seven and nine rounds of communication. So we don't have to time to explain all that. And actually, we don't even need to because the entire thing happens during the very first round. The very first round implements was called a multiplication to addition. So let's dive into that. What is it? It's a way to transform two multiplicative shares, k and x, into two additive shares, alpha and beta, where this equation applies. So how does the paper say that you could do that? It says that Alice needs to encrypt her multiplicative share k, along with zero-knowledge proof for doing just that, and send it over to Bob. Bob now can operate on this homomorphically encrypted value. He doesn't know what it is, but he can multiply his multiplicative share, his x, his key share. And to make sure that Alice doesn't learn anything about his x, because that would be terrible, he adds a mask to mask it. Now Alice decrypts this, she gets alpha, Bob gets beta by being equal to minus mask. And as you can see, it so happens that k times x equals to alpha plus beta. There's one very important thing that we need to know about this. In the case of ECDSA where x and k are 256 bits, the mask needs to be sufficiently large, needs to be bigger than 512. Now, why is that? To give an intuition of how this masking works, we're going to use some visualization of small numbers. Let's say we didn't even use a mask. If Alice would encrypt and get this value, the x times k, she knows k, right? She could just divide it by the k she knows and get x in the clear. That's why we need that mask. Now, adding the mask actually works. It masks it. Although she knows k, she doesn't know the mask so she can't learn anything about Bob's x. But what happens if that's not the case? What happens, for example, if k is even larger than the mask itself? In this case, we can see that adding the mask actually hides nothing like the most significant bits, leak x in the clear. Now, this could be a really great attack, right? Like we could be Alice, we could choose a really big k that's larger than the mask, send it over, decrypt this, and get everyone's key share in one go. But remember that zero knowledge proof that I mentioned that she's sending over, it actually also includes what's called a range proof. What that is, it's a zero knowledge proof that ensures that the number k is no bigger than 256 bits, which kind of foils our entire plan of attack here. So we're going to get deeper into the math of the zero knowledge proof. Hold on tight. How does it look like? Basically it's a bunch of numbers. The number z is the one that we'll be focused on because that's the one that relates to the range proof part of the proof. Now, how is z calculated? It's equal to w, which is a small random number, plus k, which is the number Alice trying to prove is sufficiently small, times a hash that depends on w, everything what you're doing. Now when Bob receives this, he does a bunch of verifications over the other parts of the proof that we're not going to touch on, and he makes sure that z is small. Now, as you can see, if k will be too large, then z will not be small enough and will fail the range proof part of the proof. Now, I just want to stress that Alice has to follow this specific formula for calculating z, because if he doesn't, some of the other verifications would fail. So, how can we go about cheating in this zero knowledge range proof while adhering to this formula of calculating z? What we're going to do is we're going to focus on this part of the equation, and we're actually going to try and zero it out, right? If we somehow manage to zero it out, then it doesn't matter how big k is, z is only equals to w, which is a small number, and we can use whatever big k we want and pass the verification. So, how will we do that? We're going to use what's called the Chinese remainder theorem for this use case, because, as Nikos mentioned earlier, n is a product of two primes, p and q. If this value we're trying to zero out modulo p and modulo q, they both zero out, then it also implies that the value we're trying to zero out zeroes out. So, we're going to use this lens of looking at it, and we can try to zero out the left-hand equations one by one, and by doing so, we'll achieve our goal of zeroing out the right-hand equation. So, one by one, we start with the first one, it's actually surprisingly simple, because Alice is free to choose whatever k she wants. How about she chooses k to be equal to q, right? Then, by definition, q mod q zeroes out, and we're halfway there, the easy half now. So, how do we go about the second part? How do we zero out this value modulo p? What happens if this hash that depends on w, happens to organically land on a multiple of p? Well, that would be fantastic, right? So, is it likely to happen? No, but what if we brute force w, such as the hash that depends on it, happens to be a multiple of p? Is this feasible, you might ask yourself? The question is, no, it's not at all feasible, because the paper says that n needs to be 2,048 bits, which makes p and q over 1,000 bits. Now, that's impossible to brute force, and we might as well just brute force x, which is 256 bits. So, what can we do? How about during the key generation phase, which happens once per wallet, Alice, instead of choosing n to be comprised of two equally sized prime numbers, she would choose n to be comprised of two very asymmetrically sized prime numbers, whereas p can be as small as 16 bits, which is brute forceable in an instant, and we can have q to be sufficiently large to make up for the desired size of n. Now, some of you might be thinking, well, maybe the protocol says that there's zero knowledge proof to prevent us from choosing such an n. It turns out that there is no zero knowledge proof that prevents us from using such an n. There is no zkp for no small factors. The only thing that the parties check is whether n is square free, and I'm not going to get into the technical definition of square freeness, but it sounds like, you know, we have a way to, we have a hook, we have a way to get the goods. We can inject this n with a very small p, then we can choose k to be equal to q, then we can cheat in the range proof, and then when we receive the MTA response, we can get all the x's in the clear. So, are we done? Not so fast, because remember the MTA formula, so this is what Alice obtains during the MTA. Well, in reality, there is a pesky modulo n operation happening there implicitly, and it's unavoidable. I'm not going to get into why it's unavoidable, but it's unavoidable, and it's very annoying for the following reason. k is now almost as big as n, and what happens to the leakage when k is almost as big as n? So, let's have a look at k times x plus the mask. Here we see the beautiful x and the most significant bits in the clear. However, as soon as you apply the modular operator, the most significant bits of x, they disappear, they jump off a cliff, they go bye-bye. And the only thing that remains is a small leakage of x. How small? What Alice obtains using this attack is x mod p, and p is a 16-bit prime. If only there were a way to obtain x from such remainders. Worry not, Nikos. Once again, the Chinese remainder theorem comes to our rescue. Now, let's explain how it works a bit more in depth. Let's say we have this small number x, which is 23, right? We can encode it in a myriad of ways, like we can also see in the screen, we can encode it decimally, we can also encode it hexadecimally, we can actually also use crt to encode a number. Now, how would one go about doing that? You need to divide this number by a few small primes and save the remainder. Now, just using this information, the primes and the remainders, you can feed them into crt to reconstruct the original number fully. Now, it looks a bit like magic, but it also has some limitations. Namely, it would only work to encode a number that is smaller than the product of the primes. In this example, using the primes 3, 5 and 7, you could only encode a number as big as 105. We're trying to encode a number as big as 256 bits. So to do that, we'll need 16 small primes that are each 16 bits of size. So if we can get these 16 remainders by the leakage that we explained earlier and feed them all into crt, we have Bob's quisher, right? That could work. Anything stopping us? Yes. We only have 1n. Remember, while k is chosen afresh during every signature generation, n is chosen once per wallet during the key generation phase. So we only have 1p value, so we can only do this to achieve the very first leakage. So bear with me. What if, during the key generation phase, Alice, instead of choosing n to be this handsome looking value that is a product of two primes, she would choose n to be this monstrosity that is a product of 16 small primes, plus times a prime that is as big to make up for the desired size of n. Well, some of you might be wondering, well, isn't there some zero-knowledge proof in the protocol to make sure that you can choose this monstrosity? It turns out that there is no zero-knowledge proof before checking the bipramality, because the only thing that the parties check is whether n is square free. And I'm still not telling you what square free-ness is. So we're at the finish line. We can extract all of these moduli by injecting the n and then choosing the k and then cheating in the range proof and then obtaining the leakage. So how does it work? Well, when we had the normal looking n, we said k equals q and we obtained leakage x mod p. Now that we have the bizarro n, we said k equals n over p i and we obtain leakage x mod p i. And we need to do this in 16 different signature generations, 16 different signature sessions. In the first one, we're going to use the first k, it's going to give us x mod p 1. In the second one, we're going to use the second k, it's going to give us x mod p 2 and so on and so forth until we get the 16th leakage. We feed everything into the CRT algorithm and we obtain x in the clear. So once again, we went to see if this crazy idea would work. We found an open source implementation of the gg20 protocol and we gave it a try. So we start by choosing 16 small primes to make our bizarro n and then we start signing. Each time we choose k, we cheat in the range proof and we get the desired leakage. Now we need to do this, we need to sign 16 times each time getting one of those leakages and then we can feed all of this information into CRT. Once again, combine it with our own key share and we can print the full private key in a beautiful hexadecimal format, load it up, spend away, no need for consent of any of the other parties. Once again, we've notified all the affected vendors, have since mitigated this attack. How would you go about mitigating such an attack? Well, you could add those zero knowledge proofs for making sure the well-formness of n so Alice can choose this bizarro n that is needed to carry out the attack. Well, congratulations, we're done with a second attack when you're on to the third and last. Great, so in the last few minutes we will show you how to compromise what we call a do-it-yourself protocol that we found in the wild. For this protocol, we will be exfiltrating the key in a single signature. Actually, we're going to do that in less than a signature. We're going to do it in the first round of the first signature generation. So what does the protocol look like? So it is also an MTA-based protocol. However, here there are no zero knowledge proofs accompanying the ciphertext that the party sent to each other. Furthermore, the mask now is chosen to be as big as n and this is important because now our previous attack is no longer relevant because it doesn't matter how big k is what's inside the encryption is always hiding the x. Okay, so what can we do about it? Well, we can do a lot about it because without the zero knowledge proof, Alice can send something that's not even a ciphertext. She can send, for example, the value 4. In doing so, and by choosing a carefully crafted n, specifically one that is not square free, Bob will inadvertently send back his x in the clear. Actually, it's not exactly in the clear. You need to do some data processing and there's a bunch of primes involved and you need to do CRT and then you need to brute force a bit but everything happens in seconds. Alright, so you could think about this attack as a condensed version of the previous one whereas instead of getting the 16 leakages in 16 signature, we get all of them in one signature, do the brute forcing offline and then use CRT to reconstruct the key. Now, this demo is going to go fast so do not blink. We start the signing, we do the MTA phase, we get the leakage, we do the brute forcing, we fit it all into CRT and voila, we have the private key. To conclude, we've shown you a bunch of different attacks on MPC protocol that all result in exfiltrating the private key which is bypassing the basic promise, security promise of the MPC protocols. I don't know, I hope that you might find this as interesting, as exciting as we do because this is a new space, the cryptography is really interesting and it could really use as many researchers, as many smart people's eyes looking over making sure everyone is implementing this stuff correctly. Here you can find all the proof of concept source codes that we've shown you today as well as a technical white paper explaining the math in more depth than you could have ever dreamed of and thank you very much for coming. Thank you.