Solana attack: Let’s dive in a code and find out the truth
The massive attack on Solana wallets (as well as some other networks) raised a seed of doubts in hearts of cryptocommunity. People started questioning the ciphering mechanism itself, denying most basic principles and logic.
From the Solana side there was a suggestion that the attack had been focused on IOS devices: (https://twitter.com/aeyakovenko/status/1554745536741138433?s=21&t=Y010KDQPxe3hioVlbl-fgg).
Many people started to make speculations about security in the TrustWallet. Let’s investigate the possibility that it was TrustWallet. Sir Arthur Conan Doyle once wrote:
“When you have eliminated all which is impossible, then whatever remains, however impossible, must be the truth”.
So let’s eliminate some impossibilities and clarify how the TrustWallet works with private keys and mnemonics.
00. Preparations
First of all there is no available open source code of the TrustWallet IOS application itself but we have https://github.com/trustwallet/wallet-core which is more than enough to do this research.
This library consists of C++ core logic and platfrom-specific wrappers (swift used for IOS, kotlin for Android). The IOS wallet core library is spreaded as a pods library with the name TrustWalletCore.
Our points of interest in the repository:
- Folder /swift as a general folder for IOS library, with a few subfolders
- Folder /swift/wallet-core/Solana with specific implementation of the ciphering for Solana
- Folder /swift/Sources as the folder with the KeyStore logic for IOS
- Folder /src/Keystore as a source of information how protection works in TrustWallet core
- Folder /src/Solana as a reference where we can check how sign works for the Solana network
Note: In order to work with Solana specific cryptography the TrustWallet core uses https://github.com/trezor/trezor-crypto. You can find all Solana specific entries by searching the value of the enum TWCurve (include/TrustWalletCore/TWCurve.h): TWCurveED25519.
The header for the actual implementation of the curve can be found here: trezor-crypto/include/TrezorCrypto/ed25519.h.
01. Start digging
Let’s start with the class swift/Sources/KeyStore.swift since we want to find out if it is possible to leak your private and mnemonic phrase in IOS using this library.
Let’s take a look at the load method:
This is our first stop. As we can see, TrustWallet Core works with the IOS App’s file directory where it stores some crucial information. Initiating the class will call the load method. This method tries to get all files in the App’s directory and will try to load them into the application’s memory.
Now that’s interesting. While it’s not completely prohibited to store essential information in the App’s directory on a phone, it’s highly unrecommended. At least some strong cryptography should be used. But let’s not jump to conclusions here and try to take a closer look instead.
02. Digging deeper
So, there are a few reasons why the TrustWalls team used this approach:
- First, Apple Keychain doesn’t cipher data within itself.
- And second, this data can be uploaded to the Apple servers to make it accessible through all your devices.
So probably the TrustWallet team has done it because it’s only a pods library and not the application itself. They shouldn’t make decisions about the final security design on the application. It’s something to consider for those who will use the library.
What’s important here is how the TrustWallet Core library protects the data inside the file in App’s directory. Let’s look deeper in the load process itself. The entry point for this piece of logic is here:
The logic for this process is implemented here: src/Keystore/StoredKey.cpp
Let’s check the createWithJson method.
Finally we have come to the loadJson method. And it is interesting:
So first, here we are trying to read JSON files stored in the App’s directory. And second, we can see here standard logic to read the data of JSON’s file by keys: type, name, address, accounts and, finally, payload.
And payload is the interesting key. It is the key where we can find mnemonic and private phrase. But let’s move step by step.
03. The key analysis
As we can see the library uses EncryptedPayload in order to decipher the data. The actual implementation of that class can be found here: src/Keystore/EncryptionParameters.cpp .
This is the logic that has been called:
Now we know it. So the payload is encrypted. But how and when can it be decrypted?
Let’s return to the class src/Keystore/StoredKey.cpp for a moment. We can see here that it calls the decrypt method of the payload in three different cases:
- Construction of HDWallet object (it requires mnemonic):
2. Construction PrivateKey object (requires plan text deciphered private key):
3. Some special case when it’s required to recreate public address (requires private key):
In every case it consumes some password. What is it? Let’s look at the decrypt method closer:
While you may think it’s a bit challenging to comprehend, it’s actually easier than it seems. Basically we can see here that the AES-128 has been used which literally means: Advanced encryption standard with 128-bit length key has been used here.
Note: The AES encryption method is the first (and only) publicly accessible cipher approved by the U.S. National Security Agency (NSA) for top secret information when used in an NSA approved cryptographic module. You can read the details here: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard .
What does it mean? It means that in order to encrypt the payload it is required to have a password. To make the decryption more challenging it is recommended for the password to be provided by the client. We checked the whole library and can confirm: it does not store this password.
Obviously, the recommended practice here is to ask the client to set a password and then to provide it each time when the decoding of the payload is requested.
OK, we got it. The data inside of the TrustWalletCore library should be safe and all. What’s about the export? In case when we need for instance to transfer the mnemonic and private key through some integration to another service?
Well, let’s check it out.
04. Checking out
In the class swift/Sources/KeyStore.swift we can find the method with the name export:
It says in the description: “Returns encrypted JSON key”.
It’s curious. Encrypted how? Let’s look into the exportJSON method. It is the call for the C++ logic through the mapping and the actual mapping for this method can be found here: include/TrustWalletCore/TWStoredKey.h.
And here (in the file src/interface/TWStoredKey.cpp) is the implementation:
It will call the TWDataCreateWithBytes method from src/interface/TWData.cpp :
To make it easier to understand let’s look in the autotests section for this code block: swift/Tests/Keystore/KeyStoreTests.swift:
So basically we can see here that it’s not protected by AES, since it is very challenging for all vendors to use the same implementation for the encoding mechanics, and basically can be decoded automatically. And also it is possible to export mnemonic phrases and private keys through the library.
And here is the thing.
- First, mnemonics and private keys are your property. And it remains yours while you have an ability to take it. If some service won’t allow you to get your credentials then you don’t have it. Only virtually.
- And second, there is a pretty huge problem around transfering data and storing data in different services. Those services that weren’t verified properly possibly transferring your private data as a plain text, accessible for any interceptor. And even more than that if you store at some point your credentials on a service’s backend it can be exploited and your data will be leaked.
And while we don’t have access to the IOS mobile application of the Trust Wallet, it is highly unlikely based on their public code in the repository for Trust Wallet Core that the application has such weak points that can result from a tragedy that happened recently.
On the other hand our team wasn’t able to find even a tiny piece of the public code of Slope Finance’s mobile application (https://github.com/SlopeFinance).
MoonRankNFT reported that they were able to trace decoded as a plain text users’ mnemonics and private keys being passed to Slope servers via POST messages from Slope’s wallets (see https://twitter.com/MoonRankNFT/status/1554911833617641472)
05. Conclusions
To summarize the whole situation we want to highlight some key points and share our thoughts.
We saw how professionals of the market had raised a question on the possibility for a nonce bug in Solana to be exploited. This possibility was eliminated almost instantly due to various reasons that could be explained in another article if you’d like to.
The attack was not typical and it created a panic across the community and caused massive and totally unjustified damage to the Solana ecosystem. And the crux here is the simple fact that the amount of people in our community are not educated enough and forced not to trust their own judgment but to believe other people.
Meanwhile, the base idea of the whole crypto is to how to operate without being forced to trust anyone. Furthermore, we saw how this idea was exploited by some critical service providers in the industry via their refusal to provide some core pieces of the code to the public.
To make things more ironic (in a sad way) in the documentation page of Slope Finance for developers, on a Mobile First page, there is a statement:
(see https://docs.slope.finance/introduction/mobile-first)
In the end, from a security perspective we can only add that the core of the security as a concept lies in the culture of work. Since the crypto market has an ideology and manifest, it should be followed to prevent unwanted exploitation. If it is not possible, the reason should be exposed to public discussion and be accepted by the community.
Otherwise we will see new attacks of this type in the future.
Stay tuned and find out more about us and what we provide on our: