Three lessons about hardware wallet security and their exploits

How can a Ledger sign a transaction, if it cannot even hold that transaction in memory?
Share:
Share on facebook
Share on twitter
Share on linkedin

Back in 2018, when Vacuumlabs engineers launched AdaLite as the first light wallet for Cardano, we believed that hardware wallet support was important and significant. Today, that has more than proven to be true: hardware wallets are an essential component for crypto users to safely store their tokens without 3rd party custodians, thus enabling an essential concept of decentralisation and self-sovereignty.

Today, Cardano is amongst the most valuable blockchain networks both in terms of activity and market cap, and Vacuumlabs is the official partner to offer hardware wallet support to the entire Cardano network. In the process of tinkering with hardware wallets, we have occasionally found critical flaws and also learned some interesting things about their security and ways to exploit them.

The hardware wallet memory riddle

Hardware wallets are unusual from a modern app development perspective. Take their memory, that is the most crucial and vulnerable part of a hardware wallet, since this is the place where private keys and other sensitive information is stored. In order to limit physical attacks and exploits, hardware wallet suppliers intentionally keep the memory extremely small. Ledger — one of the leading hardware wallet providers — offers wallets like the popular “Nano S” wallet with just 4kB of available operating memory. In practice, even a big chunk of that memory is used by low-level logic, the display and other functions, leaving just about 1.5kB for our app logic. That’s tiny.

In fact, the memory is so small that it is often not even big enough to hold all the elements of the transaction that it is signing. For instance: the data package of a typical Cardano transaction will often exceed the size of Ledger’s memory capacity. Which begs the question…

“How can a Ledger sign a transaction, if it cannot even hold that transaction in memory?”

In order for the wallet to be able to sign transactions, it needs to compute various components of the transaction — such as the sender address, receiver address and token amount — into a transaction hash. This is done by a hash function, such as SHA256 or, in the case of Cardano, the Blake2b256 hash function.

The solution that resolves this riddle , is that designers of most cryptographic coins will choose a hash function that can be computed in a streamed fashion. Most hash functions in practical use support that. This means that you can just pass individual, arbitrary sized chunks of data through the hash function, and keep only the partial result in memory. This way it takes only several hundreds of bytes at any point in time. The hardware wallet can then finally sign just the hash of the transaction, rather than the actual payload of the transaction.

The above description is accurate for Cardano, as well as many other blockchain tokens. It is not the only solution to the riddle though: other blockchains, such as Solana, make their transactions smaller on purpose, so it fits into a hardware wallet like a Ledger all at once.

That time a Vacuumlabs engineer uncovered a critical Ledger vulnerability

Since Vacuumlabs is the dedicated partner for the Cardano network to add hardware wallet support for the Ada currency, a small group of Vacuumlabs engineers set out to enable Cardano support for both Ledger and Trezor applications.

As alluded to before, we found only 4kb of RAM memory available on the Ledger hardware wallets, of which about half is used simply by the system/library memory to power the Ledger.

In order for us to enable Cardano support, we had to be incredibly thoughtful about optimising our memory usage. Normally, one would sign transactions and then sign a message, where a message is some form of note represented as a string.

But here is where things get interesting: when a user would sign a transaction, the display of the ledger would show that you are signing the transaction, however doing that by only showing the message as a “signing hash”, that a user would approve to complete the process. All good, or so it seems.

As it turned out, Ledger would actually use the same part of memory for messages and transactions, allowing a malicious actor to potentially hide a secondary transaction in the message with neither the Ledger nor the user knowing this was happening, thus being able to funnel currency away through this backdoor potential secondary transaction.

Vacuumlabs engineers working on the project of enabling Cardano Ledger support were able to stage exactly such an exploit and thus prove this then-unknown Ledger exploit. As good practice holds, the loophole was reported to the Ledger team, and the vulnerability subsequently closed. The only thing that remained for us to do — after the vulnerability had been patched — was give an internal presentation to curious Vacuumlabs engineers.

The coolest hardware wallet feature: mnemonic derivation

To wrap it up, we want to bring your attention to the feature that we — eb— think is one of the coolest features or principles behind hardware wallets: mnemonic derivation.

To start with the basics: a mnemonic phrase is a set of words that forms a human-readable form of a random number or computer-readable hash. A common protocol for generating mnemonics is the BIP39 specification. It takes a point of randomness, and turns it into a mnemonic phrase that is easy for humans to handle. Based on this mnemonic, a hash function can then derive a seed, which is a hash of a particular length (mostly 32 or 64 bytes).

BIP39: Mnemonics for Recording Long Keys | smarx.com

From this seed in turn, a root keypair of a public and private key is generated. Using this key pair, a child key pair is in turn derived for each cryptographic asset or coin you would like to hold: one key pair for Bitcoin, another for Ethereum, yet another key pair for Cardano, and so forth. In fact, at any point in the future can you generate a key pair for tokens you might not currently hold, or that might not even currently exist yet. But it doesn’t stop there: based on the key pair for a particular cryptographic wallet (like your Cardano wallet), key pairs are generated at the address level, so that all these addresses deterministically are tied to the parent account.

So why is all this weird cryptographic wizardry significant, and why are we excited? Because it allows you to encode all your addresses across all blockchain networks (even the ones you don’t have yet) with a single seed. It’s the master key, which will unlock everything. It encodes all your keys for all your tokens on all blockchains, including all your future tokens on future blockchains, too!

It goes without saying that it’s greatest advantage is also it’s great weakness: one thing gives access to all. In the decentralised world of blockchain, there is — by design — no trusted third-party bank or tech behemoth to offer 2-factor authentication or other reliable ways to recover funds. If the master key gets compromised, everything gets compromised.

Would you like to learn more about how mnemonic derivation works? Check out the BIP39 spec or introductory articles like this or this, and find your own way down this technical rabbit hole.  

At Vacuumlabs we have built a culture where tinkering with interesting and unusual technology is part of the company DNA. We think hardware wallets are cool tech, and we’d love to connect with others who feel the same. Click here if you’re interested in learning more about working with us. 

Share:
Share on facebook
Share on twitter
Share on linkedin

Related posts