
As a continued extension of our decentralized partner innovation ecosystem, I am excited to announce that we have partnered with Panther Protocol to deliver increased privacy enablement as we move forward with delivery of data driven solutions within the US and the UK.
One of the core tenets of the Kudelski ecosystem has always been secrets management within chips, root of trust (RoT), protection of digital artifacts, and ensuring the safety of our customers.
Extending our partner network into the blockchain ecosystem with Panther’s privacy preserving protocol accelerates our ability to bring data marketplace, data monetization, and DeFi enabled ecosystems more quickly to market and to offer more advanced service and build capabilities.
Our first expansion of these concepts will be into the UK market where we will work with the Panther team as well as their privacy-first Web3 development partner Stelium to unlock value within data inside the UK economy.
As we develop this relationship expect some thought leadership pieces as well as some exciting technology advancement as we explore privacy-first architectural advancements in wallets, key management, and scale.
Welcome Panther to the Kudelski Partnership Network!
To All Expert Blockchain Companies, Who May Be Interested in Joining our Partner Pool…
Watch out Decentralized Finance, here comes Decentralized Partner Innovation (DEPI)!
The “Speed of Crypto” is honestly at a level none of us have seen before. Even though we employ a team with deep expertise across many cryptocurrency technologies and chains, no one organization (even one in 30+ countries) can hope to keep up with the fast-paced changes we’re experiencing.
Our business is literally on fire. We are finding more situations where we have to either scale beyond our current team to meet the needs, or augment our team with specific expertise we don’t already have.
To help us meet these needs, we have built a model (and invented yet another acronym!) to utilize experts as part of an expanded team of decentralized partners. DEPI will help us deliver world-class security capabilities and meet the ever-expanding needs of our global client base. These partner organizations and/or individual contributors are vetted and bring expertise or parallel/specific expertise to complement or enhance our abilities to help on these very specialized projects.
There are a lot of reasons that we chose to build a decentralized partner team.
First – we can’t be in every country and meet every employment obligation globally… It isn’t feasible and just doesn’t make sense. (Plus – I need to sleep every once in a while…)
Second – some of the best talent in the crypto market have done very well for themselves and do not work for ANYONE. But, while these people enjoy their independence, they also appreciate having access to a larger organization that can offer interesting and challenging projects. This becomes a win-win marriage for both parties – providing them stimulating engagements while enabling us to meet our client needs.
Third – We have high standards. We never skimp on quality. Demand for our services outstrips supply, so we look to expand our resource pool rather than cut corners to save time and be able to deliver against the growing number of projects. We verify that every partner member of this network has a high level of expertise and delivers top-of-the-line quality. In fact, we are so confident that we have done a good job screening these partners that any work that utilizes our partners is under contract with Kudelski, with the concomitant safeguards, Terms and Conditions, and logo. So, anyone engaging our services gets the deep expertise and backing of Kudelski along with the latest in cross-pillar, highly focused expertise required in these fast-moving times.
“It’s hard to find a company that knows how to do this” is something I hear, LITERALLY, every day. I believe that this model will allow Kudelski Security to be the organization that knows how to deliver as well as having the capacity to do so.
Some of the partners that have agreed to add to their teams, or supply team members, to our pool of talent are:
As we continue to grow and scale, we will continue to add to our pool of experts as needed.
Are you interested in joining our team or being a node in our decentralized pool of talent? Please contact me here!
The HACK@CHES 2021 phase I competition happened from June 17 to August 16, 2021. During the competition, a bundle was given to the participants with a set of Verilog design files of a System on Chip (SoC). The goal was to discover vulnerabilities and report them to the judges of the CTF. According to a scoring system, a number of points was attributed for each vulnerability reported. Additional points were given if an exploit was provided or if the weakness was located in the ROM. The best teams were selected to Phase II of the contest which happenned during the CHES conference.
The SoC is based on the OpenPiton processor, an open source, processor which uses CVA6 64-bit RISC-V cores. The design files were available in the bundle and it was possible to simulate them by software using Verilator simulator or using a FPGA board. The SoC implements many peripherals among them there are three AES cores namely AES0, AES1 and AES2, a TRNG core and a RSA core.
We were granted 60 points for a vulnerability discovered in the AES0 module which is the maximum number of point possible for a unique bug not located in ROM. The following details our findings and more generally how to simulate fault attacks in hardware design to reveal hardware weaknesses.

One important step during hardware design is the simulation. Since hardware is less easily patchable than software, the hardware designs are heavily tested before the tapeout. Basically, Verilator is an open-source simulator which transforms a Verilog design into a C++ program which keep track of the execution cycles of the original design. It means that it is possible to simulate the design in software and see the timing of each execution part and the output result of a module.
It was possible to emulate the full SoC in software but the simulation was very long to run. In our case we were interested by AES0 thus we simulated only this design. The design files were located in the folder /piton/design/chip/tile/ariane/src/aes0. AES0 is a peripheral implementing AES-192. The top level module is called aes_192_sed, it has as inputs a 16-byte plaintext, a 24-byte key and a start signal. As output, when the out_valid signal is high, the result contains the AES encryption results.
We have set-up a Git repository with all the files needed for the simulation. We create a simulation program simulation.cpp which is in charge to feed the inputs to the module and clock the module until we get an output. Then, using Verilator is similar to compiling with GCC:
$ verilator -cc aes_192_sed.v -f input.vc --Mdir build -o simu --exe simulation.cpp
make -C build/ -f Vaes_192_sed.mk simuThe input.vc file contains all the module need for the simulation. Then we are able to verify the AES0 simulation works properly:
$ ./build/simu
[+] Simulation with Verilator
Using key:
8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b
Using plaintext:
6bc1bee22e409f96e93d7e117393172a
Resulting ciphertext:
bd334f1d6e45f25ff712a214571fa5ccSo far we are able to simulate properly the AES0 module.

The idea of fault simulation is not new, we already developed a tool called Glitchoz0r 3000 presented during R2Con 2020. The idea of this tool is to emulate a firmware using radare2 ESIL and try to inject a fault in registers or instructions at each step to see if a security mechanism can be bypassed or corrupted. Another tool called FiSim is developed by Riscure company and does similar tests using Unicorn Framework and the Capstone disassembler. Finally another tool called VerFI allowed to simulate fault in the netlist of a design but we need first to synthesis it which was not what we wanted to do. In our case, we were interested to have a pure software solution simulating faults using directly Verilog hardware designs.
Since Verilator was already available for the SoC, we based our solution on it and we created an executable we named Verilaptor.
One standard hardware attack of AES is differential fault analysis (DFA). The idea is to introduce fault at the last rounds of AES and collect the faulted ciphertext to recover the secret key. To have the attack successful, we must inject faults at the output of the AES Sbox. In the design this is implemented in the module table_lookup in the file table.v. However, to optimize the simulation execution, Verilator would not let us access internal signal of the design expect if we explicitly tell Verilator. To do so, we added the comment /*verilator public*/ after the signals definition in order to have access to those signals from our simulation program:
module table_lookup (clk, state, p0, p1, p2, p3);
input clk;
input [31:0] state;
output [31:0] p0, p1, p2, p3/*verilator public*/;
wire [7:0] b0, b1, b2, b3;In Verilaptor, we create a function which simulates a random fault after the Sbox operation during round 10:
void tick_fault_r10(std::unique_ptr<Vaes_192_sed>& top, int sbox_num, int value) {
top->clk = 0;
top->eval();
// Inject fault at output of sbox
switch (sbox_num) {
auto sbox = top->aes_192_sed->uut->r10->t0;
sbox->p3 = sbox->p3 ^ value;
...A random value is XORed to the sbox outputs. Thus when executing Verilaptor, we were able to obtain faulted ciphertexts:
$ ./simu/build/veriraptor -v
[+] Fault simulation with Verilator
Using key:
d3b80fd1a0b09cefc4d343c0a7dac0b1942ca63151a89b91
Using plaintext:
f8a53552683866603d9a7dfe5982bc6f
Getting ciphertext:
5a802cf68638a0ee341e3ee25201ae1a
5a802cf68638a0ee341e3ee25201ae1a
5a8064f6860fa0ee7d1e3ee25201ae43
5a80c0f68649a0ee5d1e3ee25201ae5a
5a802cd986384eee34193ee21a01ae1a
5a802c42863896ee349d3ee2a301ae1a
a2802cf68638a0d9341ea1e25269ae1a
87802cf68638a069341e83e25263ae1a
...Then the faulted outputs were collected in files in order to recover the AES round keys by differential fault analysis. Currently the signals to fault are hardcoded in our simulation. An interesting automation of Verilaptor would be to iterate over a list of internal signal and fault each of them. Then it would allow to simulate fault attacks against various design like RSA.
The standard DFA against AES was well documented in the past for example, Quarkslab gave a complete description of DFA when applied to White-box cryptography for all variant of AES. In our case we were attacking the 192-bit version of AES. This works basically the same as for AES-128 but the key schedule algorithm of AES-192 is a bit different. To revert it properly we need the last round key and half of the previous round key. Thus the idea is to perform the standard DFA against the last round key, the 13th. In order to do that, the corruption needs to happen during round 11. These faults are collected in a file and then feed to PhoenixAES tools from the Side-Channel Marvels which implements the DFA:
print("[+] DFA on 13th round\n")
subkey13 = phoenixAES.crack_file("tracefile_r11", verbose=0)Once the 13th round key is recovered, it would allow to revert the last AES round. Then we are able to perform again a DFA attack against the previous round to recover the 12th round key. However, there is a trick to attack the 12th round key, we are now attacking a round with a MixColumn step which was not the case for the last round key. However, PhoenixAES v0.0.4 is able to handle that for us just by passing the previous round key as an argument (Thanks @doegox for the remark and the bugfixes):
print("[+] DFA on 12th round\n")
subkey12 = phoenixAES.crack_file("tracefile_r10", lastroundkeys=[unhexlify(subkey13)], verbose=0)Thus, the full attack allows to recover the two last round keys:
$ python3 attack_hack_ches21.py
[+] DFA on 13th round
Last round key #N found:
83FD2BED375F3431BFE5939B188C895B
[+] DFA on 12th round
Round key #N-1 found:
88BAA7AAA7691AC01614D51D28A7F490
Concatenated round keys:
88BAA7AAA7691AC01614D51D28A7F49083FD2BED375F3431Finally, Stark is a really convenient tool, from round keys it reverses the key schedule algorithm and recover the secret key, the first round key:
$ ./Stark/aes_keyschedule 88BAA7AAA7691AC01614D51D28A7F49083FD2BED375F3431 11
K00: D3B80FD1A0B09CEFC4D343C0A7DAC0B1
K01: 942CA63151A89B9110AC8E00B01C12EF
K02: 74CF512FD315919E473937AF1691AC3E
K03: 933D3C4723212EA857EE7F8784FBEE19
K04: C3C2D9B6D55375887AA0F8445981D6EC
K05: 0E6FA96B8A94477249569EC49C05EB4C
K06: 1949D19A40C807764EA7AE1DC433E96F
K07: 8D6577AB11609CE7D9974518995F426E
K08: D7F8EC7313CB051C9EAE72B78FCEEE50
K09: 72BF166BEBE054053C18B8762FD3BD6A
K10: B17DCFDD3EB3218D5F424BD9B4A21FDC
K11: 88BAA7AAA7691AC01614D51D28A7F490
K12: 83FD2BED375F3431BFE5939B188C895BThe full attack is implemented in the attack.sh script of our repository. It runs the fault injection and DFA on the faulted results.
We found the approach really interesting since Verilator allows to easily test a hardware design purely in software and mount fault attacks against it. We found this approach quite generic and we think it could be adapted to tests various hardware designs to test some countermeasures developed to thwart fault attack and see if they resists in simulation before the design is deployed in the field. The same approach could be done for timing attack simulation since the simulation is cycle accurate.
Solana is a web-scale, open-source blockchain protocol that is fast, secure, and fully decentralized. The protocol introduces eight core technologies that provide the infrastructure necessary for DApps and decentralized marketplaces. Solana uses a combination of proof-of-stake (PoS) and proof of history (PoH) consensus mechanisms to improve throughput and scalability. Consequently, the network claims to support 50,000 transactions per second (TPS), making it the fastest blockchain in the world.
In this post, we will talk about Solana program security, especially some common security vulnerabilities in Solana programs. This blog assumes advanced knowledge of the Solana program library and some basic understanding of Rust.
Introduction to Solana Programing Model
Smart Contracts in Solana are written in Rust or C, and they are called Programs. Solana Programs can own accounts and modify the data of the accounts they own. Other than on-chain programs which are developed and deployed by a Solana programmer, there are several native programs, which are required to run validator nodes. One of the native programs is the System Program. This program can create new accounts, allocate account data, assign accounts to owning programs, transfer lamports from System Program owned accounts and pay transaction fees.
Program id: 11111111111111111111111111111111Instructions: SystemInstruction
Solana accounts
An account in Solana contains several fields such as owner, data, lamports, executable … which are set by the system program. If the program needs to store state between transactions, it does so by using data field of the account. Here, we provide some examples for accounts. You can see that account_1 and account_2 are owned by a token program and a stake pool program, respectively. The data of each account is therefore specified and updated by the owner program. Here is more detail on Solana accounts https://docs.solana.com/developing/programming-model/accounts.
System_Program -> account_1 -> {owner = token_program_id, lamports, executable, rent_epoch, data -> Account {owner, state, mint…} } System_Program -> account_2 -> {owner = stake_pool_program_id lamports, executable, rent_epoch, data -> Stake_pool {manager, state, staker…} }
Solana Program flow
An app interacts with a Solana cluster by sending it transactions with one or more instructions. The Solana runtime passes those instructions to programs deployed by app developers beforehand. These instructions will be executed and validated by Solana Validators.

What can go wrong?
It is important to note that the Solana transaction structure specifies a list of public keys and signatures for those keys and a sequential list of instructions that will operate over the states associated with the account keys. This helps optimize the throughput, but because of this, malicious users can input arbitrary accounts, and it is now the program’s job to protect its state and data from the malicious input accounts.
Writing a Solana program is pretty simple if you know Rust or C, and understand the Solana programming model. Here are some examples (https://docs.solana.com/developing/on-chain-programs/examples). However, writing a “SECURE” program on Solana is non-trivial. As we discussed above, the program can write anything to the data of accounts it owns. Therefore, it is program’s responsibility to validate and protect its input account data.
We want to discuss two types of validations that are important and can be exploited (that may lead to loss of funds) if the program does not validate inputs properly.
1, Account ownership validation
One of the most important validations is to check whether the owners of the input accounts are the expected owners. For example, Solana stake-pool program needs to perform the following checks for every input stake account in order to ensure that input stake accounts are owned by the Solana stake program as expected.
/// Check stake program address
fn check_stake_program(program_id: &Pubkey) -> Result<(), ProgramError> { if *program_id != stake_program::id() { msg!( "Expected stake program {}, received {}", stake_program::id(), program_id );Err(ProgramError::IncorrectProgramId) } else {Ok(()) }}
We note that without the check_stake_program, it is possible for a malicious user to pass in accounts which are owned by a malicious program. Similarly, this function checks if the input accounts are indeed owned by the Solana system program.
/// Check system program address
fn check_system_program(program_id: &Pubkey) -> Result<(), ProgramError> { if *program_id != system_program::id() { msg!( "Expected system program {}, received {}", system_program::id(), program_id );Err(ProgramError::IncorrectProgramId) } else {Ok(()) }}
2, Account state (data) validation
Missing account state (data) validation is probably one of the highest severity mistakes that developers can easily make. For example, data of a reserve associated to a particular lending market in Solana token-lending program is specified by this struct. These fields will be instantiated when the reserve is initialized.
pub struct Reserve {/// Version of the struct
pub version: u8,/// Last slot when supply and rates updated
pub last_update: LastUpdate,/// Lending market address
pub lending_market: Pubkey,/// Reserve liquidity
pub liquidity: ReserveLiquidity,/// Reserve collateral
pub collateral: ReserveCollateral,/// Reserve configuration values
pub config: ReserveConfig,}
So whenever the reserve is updated or used by a lending market, the program has to ensure that the input lending market is the one that the reserve has instantiated with. Otherwise, a malicious lending market can access the reserve and may be able to drain all funds. This check was missed in Solend, and lead to a potential loss of 2 millions (https://docs.google.com/document/d/1-WoQwT1QrPEX-r4N-fDamRQ50LM8DsdsOyq1iTabS3Q/edit#). Fortunately, the Solend team was able to detect and stop the exploitation in time such that no funds were stolen.
if &reserve.lending_market != lending_market_info.key { msg!("Reserve lending market does not match the lending market provided"); return Err(LendingError::InvalidAccountInput.into());}
Another example in Solana stake-pool program, a stake-pool is specified by this struct and these fields will be instantiated with some account pub-keys when the pool is initialized.
pub struct StakePool { pub account_type: AccountType, pub manager: Pubkey, pub staker: Pubkey, pub deposit_authority: Pubkey, pub withdraw_bump_seed: u8, pub validator_list: Pubkey, /// Reserve stake account, holds deactivated stake pub reserve_stake: Pubkey, ...
After the initialization, processes that involve the reserve stake account such as process_increase_validator_stake, process_update_validator_list_balance, process_update_validator_list_balance, and process_deposit have to check if the input reserve account is the same as the reserve_stake of the stake-pool.
pub fn check_reserve_stake(&self, reserve_stake_info: &AccountInfo, ) -> Result<(), ProgramError> { if *reserve_stake_info.key != self.reserve_stake { msg!( "Invalid reserve stake provided, expected {}, received {}", self.reserve_stake, reserve_stake_info.key ); Err(StakePoolError::InvalidProgramAddress.into()) } else { Ok(()) } }
Conclusion
These two types of mistakes are very common, easy to make, and can potentially lead to loss of funds. Therefore, it is necessary to ensure that account owners and account states are validated before deploying the programs to the Solana main chain. In future blog posts, we will discuss some more advanced concepts in Solana as well as some other security vulnerabilities.
Most security experts are by now aware of the threat that the rise of quantum computing poses to modern cryptography. Shor’s quantum algorithm, in particular, provides a large theoretical speedup to the brute-forcing capabilities of attackers targeting many public-key cryptosystems such as RSA and ECDSA. But how much, exactly, is the impact in terms of security? What is the timeline we have to expect? Which algorithms are more vulnerable than others?
In this post I am going to deep-dive in quantum attack resource estimates for public-key cryptography based on recent advances on the topic. In particular, we will see the exact computational cost of running Shor’s algorithm to attack different keysizes for different public-key algorithms, we will put this in context with the current state of quantum computing, and we will see how RSA will be broken by June 25th 2027 (just kidding!)
First of all we need to quickly recap, from a purely classical standpoint, the difference between various computational hardness assumptions used in modern cryptosystems. This is actually a very complex topic. For the sake of simplicity, in this blog post we only take into account the following three problems:

















Actually, taking into account these three problems in the context of quantum security is an oversimplification. In fact, in reality and unlike very often and wrongly claimed in popular literature, most existing cryptographic schemes are not directly based on the above assumptions. For example, the security of RSA is not based on IFP, but rather on a similar hardness assumption, called “RSA assumption”, that is known to be reducible to IFP, but the vice versa is not known. This means that “breaking RSA” is at most as difficult as solving the IFP, but it might be easier than that, just so far nobody figured out an easier way. The same happens for most schemes “based” on discrete logarithm problems, such as Diffie-Hellman key exchange or ECDSA elliptic-curve signatures. However, from a practical standpoint, most of the modern cryptanalytic efforts in breaking these schemes focus on solving the above math problems, so this is what is going to be relevant for us when looking at quantum attack resource estimates.
So, how large (again, from a non-quantum perspective) should, e.g., an RSA or ECDSA key be? It depends from two things:
For factorization and finite field discrete logarithm the situation is similar: the best known algorithm for solving these two problems in the cryptographic case (Number Field Sieve and Index Calculus Method, respectively) have a similar asymptotic subexponential complexity that can be roughly approximated as
![\mathcal{O}\left( 2^{9\sqrt[3]{n}}\right)](https://cdn.prod.website-files.com/67711be5796275bf61eaabfc/685cff0e76a70033d327b1f3_latex.png)
for

-bit moduli. This means that targeting

bits of security for cryptographic schemes such as RSA and DH requires pumping up the key size quite a lot: 2048 bit for 112 bit of security, 3072 bit for 128 bit of security, 7680 bit for 192 bit of security, etc.
For the ECDLP problem, instead, the best known general solving algorithm (Pollard’s Rho) has an exponential complexity of roughly

for

-bit curve fields. The lack of known subexponential attack algorithms is basically what made elliptic curve cryptography attractive so far, because the resulting key can have much smaller size for the same level of bit security, with corresponding increase in bandwidth and efficiency. This is summarized in the table below.
However, as we will see, this is also what makes elliptic curve cryptography more vulnerable to quantum computing.
Now we look at the quantum scenario, i.e., we consider a situation where a large scalable quantum computer has been built and is able to run complex quantum algorithms. It is well-known that Shor’s quantum algorithm can solve the integer factorization problem in polynomial time. Let’s dig a bit deeper in this claim.
First of all, Shor’s algorithm is actually composed of two parts: a purely quantum part (Quantum Fast Fourier Transform, or QFFT in short) and a purely classical pre- and post-processing phase. From a complexity standpoint, the QFFT has polynomial time complexity of roughly

for

-bit input integers. Given that the classical part has a similar complexity, we will only consider the quantum complexity as relevant.
For factoring

-bit integers, a quantum computer running Shor’s algorithm will clearly need at least

(logical) qubits of quantum memory to represent the integer, but it will also require additional working quantum registers that make the total qubit count increase. Given that the qubit count can be seen, as a first approximation, as a limiting resource for the attack, circuit versions capable of running the QFFT minimizing the number of required qubits have been proposed. The current state of the art of these circuits requires a number of qubits that is roughly double the bitsize of the input integer.
As in the classical case, again the situation for IFP and DLP is similar in the quantum scenario: Shor’s algorithm can be generalized to discrete logarithms, and the QFFT can be used to solve the DLP over

-bit moduli using roughly

logical qubits and with the same polynomial time complexity of roughly

.
The situation is slightly different in the case of elliptic curves. Again, the QFFT can be used to efficiently solve the ECDLP in roughly cubic time as above, but because of how the classical part of the algorithm needs to embed curve points representation into a larger group, this time the number of qubits required is roughly ten times the bitsize of the curve field!
Now, you might be tempted to say that the above implies that elliptic curve cryptography sounds more resilient to quantum attacks than RSA or DSA, because mounting an attack requires more qubits. But if you paid attention you can already see why it is exactly the opposite!
In fact, remember that elliptic curves keys are small because the best classical attacks are more inefficient. This difference disappears in the quantum scenario, as quantum attacks have all similar time complexity. And since RSA and discrete log cryptosystems already needed to use large keys in order to resist classical attacks, the required qubit count is actually larger for them! This is summarized in the table below.
As you can see, at the increase of the security parameter, the number of qubits necessary to attack RSA grows much faster than for an equivalent attack on elliptic curves of the same classical security level. We can almost say that classical attacks made RSA and related “more resilient” also to quantum attacks.
This should not be interpreted as saying that RSA is secure against quantum attacks: none of the schemes we are considering here are. However, so far it looks like EC-based cryptography is much more vulnerable than RSA to quantum attacks, and given the steady raise in the number of qubits available for quantum computation, it looks like elliptic curve cryptograhy will likely fall much earlier than other schemes.
Things get a bit more complex than that. Ultimately, we are interested in the question: “how much quantum resources we need to mount an attack against a certain scheme?” Answering this question will require us to dig a bit deeper.
We need to remember that a quantum computation is made of several “layers”:

Clearly, any improvements on any of these layers will yield an increase in the efficiency of quantum attacks, and a lower amount of quantum resources needed to mount the attack. Assessing the necessary amount of resources is therefore tricky, as this is a moving target. Regardless, something can be said about it, and a few recent works shed new light on the topic.
First of all one has to consider that the number of qubits necessary to mount the attack might not be the limiting factor. The estimates provided in the previous section of this blog post are lower bounds that take into account the minimum number of logical qubits required, but this is not an universally accepted measure of hard resources. In fact, there are better ones: If we consider the full implementation stack from the bottom layer of physical qubits up to the algorithmic layer, we see that certain common elementary quantum gates are more expensive than others. Pauli gates are usually “cheap”, in the sense that they are easy and fast to implement, while T gates (Toffoli) are extremely hard. They are so hard in fact that, as a first approximation, one can simply disregard every other quantum gate, and only count the number of T gates as a measure of the complexity of running a quantum algorithms. The “T count” complexity reflects exactly this: the higher the T count complexity of a quantum algorithm, the more difficult it is to build a quantum computer able to run it.
Given the above, quantum compilers usually allow to operate in a mode that produces a circuit representation that does not optimize the number of logical qubits, but rather the number of T gates. This usually has a side effect: a large increase in the number of physical qubits required (because converting from physical to logical requires T gates) and also an increase in the running time overall. It is also wasteful if a certain T gate is only used once on a certain qubit, while it could be better to reuse them as much as possible once they are implemented. For this reason, another very useful metric is the so-called “T depth” complexity, which takes into account the fact that a circuit can be described in “layers” where many T gates can be used in parallel on different qubits.
Building circuits that optimize the T-count or T-depth might end up using more logical layer than the strict minimum, but it would result in a more efficient attack overall, because it minimizes the real-world resources (time and cost of implementation). Recent works in quantum cryptanalysis adopt this approach, and new results have been published recently. The tables below show the state of the art in quantum attack resource estimates for two different scenarios: the current (realistic) case of base noise error of

in the underlying physical qubits (achieved by current state of the art superconducting qubits), or the more optimistic case of an error rate of

that most experts seems to agree it might achievable in the short-term future. Notice that the minimum number of logic qubits has increased compared to the previous table because, as mentioned before, recent results in quantum optimization aim at minimizing T-count and T-depth rather than circuit width. Moreover, the time necessary to mount an attack greatly depends on the failure rate of a single run of the underlying algorithm, which itself depends on the level of purity achieved in the error correction mechanism, which itself depends on the number of physical qubits used in the surface code. As it turns out, the ratio between number of physical qubits employed and time necessary to run the attack is pretty constant in each of the scenarios below. For this reason, a new measure of quantum resource has been introduced: megaqubit days. This is the number (expressed in millions) of physical qubits necessary to run the attack in 24 hours given a surface cycle (“quantum clock”) of 200 ns (5 MHz) and an error rate of

or

errors per measurement.
Notice the following, interesting fact. For “low” security parameters (e.g., RSA-3072 and ECDSA-256) attacking RSA is actually less expensive than attacking elliptic curve cryptography. This is a consequence of recent results in the optimization of Shor’s algorithm for factorization. However, as the security parameter increases, we see a steady increase of the quantum resources necessary for attacking RSA, while attacking elliptic curve cryptography becomes relatively easier.
Recent results in quantum attack resource estimates have started to shed light on how vulnerable, exactly, is conventional cryptography to the ever increasing performance of quantum computers. The traditional view that “elliptic curve cryptography is much more vulnerable to quantum computers than RSA and discrete log” still holds, sort of, but the cut-off point has been moved to roughly 160-bit of classical security, while for 128 bit of security the difference is not so relevant. This is a moving target, mainly given by recent optimization on Shor’s algorithm for attacking IFP and DLP, so the real cost of attacking ECDLP might be lower. Further results can surely change the current estimates. What is clear is that with a large enough number of physical qubits all these cryptosystems can be attacked in a matter of hours.
The situation for symmetric-key cryptography is radically different, but still recent advances in quantum resource estimates contributed to better frame the impact that quantum computers might have on the practical bit-security of primitives such as AES and SHA-3.
Intro
Unless you’re living under a rock, you might have read that last Tuesday the largest “crypto hack” in history targeted Cross-chain decentralized finance (DeFi) platform Poly Network, and allowed an undisclosed attacker to steal the equivalent of a whooping 610 million USD of crypto tokens.
The situation is in rapid development, and preliminary analysis of the attack started to circulate in the last hours. In this post, I am trying to explain what happened based on the existing available information.
The very basics
In this blog post I assume familiarity with the basic working of a permissionless decentralized ledger, and in particular with the concepts of blockchain and consensus, and related cryptocurrency token applications (for instance, Bitcoin), but I will recap anyway the most basic concepts.
In a nutshell, we can say that a “cryptocurrency token” is a virtual amount of “something” that is owned by someone. You claim possession of a token by publishing a “wallet address” (basically, a public verification key) and by “doing something” that makes the consensus protocol agree to bind the token to that address. Proof of possession of that token and the possibility of transferring the token to some other wallet is provided by a digital signature, which is generated by the wallet’s secret signing key. Issuing a “transaction” simply means embedding some metadata and a related valid signature in the blockchain, which authorizes transfer of funds from wallet A to wallet B. In a fully distributed ledger, the consensus mechanism makes it so that it is in the “best interest” of the participants to accept a valid signature within the blockchain.
Smart contracts
The next step is to realize the following: As explained above, issuing a cryptocurrency transaction is basically a process where multiple participants “agree” to change the state of a virtual ledger in the same way. And a ledger is nothing else than a replicated database with entries of the form “address: amount”. However, there is nothing special in this particular form of entries: nothing described so far forbids to apply the same process to change the state of a different kind of replicated database. What if we consider as our “database” the memory of a virtual machine?
This is where the idea of “smart contracts” came into play, initially adopted within the Ethereum application. In a nutshell, a smart contract is a piece of software that is designed to run on a distributed virtual machine. On traditional computers, software is “executed” by changing, step by step, the state of the computer’s internal memory according to a set of predefined rules. A smart contract works in a similar way: the initial internal state of the machine is embedded in the blockchain ledger by issuing a transaction (from a creator’s wallet) that contains a compiled (“compressed” and machine-readable) version of the code. The code is executed by changing the state of the blockchain, and the consensus protocol makes it so that it is always in the best interest of the participants to execute the code (that is, applying the rules that govern the state change to the next block) in a correct way. As in cryptocurrency transactions, the execution of a smart contract is authorized by a digital signature issued by the “owner” of the smart contract (usually, but not always, its creator), but must be validated by the consensus.
This brings up the important concept of ownership and interaction between contracts. How do smart contracts interact? In a typical code environment, you might have different subroutines calling different functions, often collected into libraries, and you need to manage input and output of these. Smart contracts work in the same way, but the difference is that, given the distributed nature of the computation, safeguards have to be put in place (and enforced by the consensus mechanism) to make sure that, for example, if contracts A and B both call a function F, F will return to each of them the right output, or that if you create and execute a smart contract, I cannot change arbitrarily its state since I’m not the owner.
In practice, smart contracts (or their running platforms) usually embed safeguards of the form “If the caller of this program is not authorized by a certain key, then abort”. The authorization does not always come in the form of a direct signature: a contract A might be issued specifying another contract B as its “owner”, and different permissions can be given to different sets of users.
Cross chain networks
There is already too many blockchains out there to keep track of them all. A common problem that arises in the cryptocurrency scenario is when you own a certain kind of token (say, Bitcoin) and you want to convert it to another one that uses a different kind of blockchain technology or consensus (say, Ripple). This usually involves swapping tokens at an exchange, with associated fees. In the smart contract landscape, you run into similar problems: what if you are running a smart contract on Ethereum but you want it to interact with another one, say, on Cardano?
Now, if you think about it, you can see a cryptocurrency exchange as a sort of contract: I give you X Bitcoin and you give me back Y Ripple. Analogously, centralized services can act as “cross-chain interpreters” by, let’s say, allowing a program A running on Ethereum to interact with another program B running on Cardano in the following way:
Services of this type are sometimes called “oracles” and, like exchanges, require a fee to operate. Also, like exchanges, they require the user to fully trust a single entity. However, the next observation is: since these nodes are basically running a contract or script, why don’t we decentralize that one as well as we do for smart contracts?
Enter the concept of “cross-chain network”, which is basically an additional abstraction layer on the concept of distributed virtual machine. A cross-chain network allows two different blockchains to “communicate with each other”. Or, to be more precise, allows users of a certain blockchain to perform operations on another blockchain in a distributed and autonomous way. However, exactly because they operate across different blockchains, these networks usually require their own “virtual blockchain” to run smart contracts that govern the communications rules within the underlying networks.
Poly Network is a cross-chain network that sits on top of different blockchains, including Bitcoin, Ethereum and Elrond. To overly simplify its architecture, Poly can be described by the following components:
It is common for cross-chain networks, including Poly, to store at any moment large amount of liquidity in their underlying wallets, because there are many users performing cross-chain operations at the same time. Therefore, it is crucial to properly secure the very privileged cross-chain smart contracts that administer these wallets. Unfortunately, this is exactly what did not happen here.
The hack
On August 10th, Poly Network reported that an undisclosed attacker hacked a smart contract of the network, transferring the equivalent of roughly 610 million USD (mainly in Ether, Binance Coin and USDC) and moving them to external wallet addresses.
According to cybersecurity firm SlowMist and security researcher Kelvin Fichter, the hack was made possible by a mismanagement of the access rights between two important Poly smart contract. The first one is EthCrossChainManager and the second one is EthCrossChainData.
Let’s first talk about EthCrossChainData. This is a very high privileged contract that is not supposed to be invoked by anyone within the network, except by its owners. The reason is that this contract is responsible for setting and managing a list of public keys of “authenticator nodes” (Keepers) that manage the wallets in the underlying liquidity chains. In other words, EthCrossChainData can decide who has the privilege of moving the large amount of funds contained within Poly’s Binance wallet, Ethereum wallet, etc. If an attacker could call the right function (putCurEpochConPubKeyBytes) within EthCrossChainData, there would be not even need to attack a Keeper’s secret key: the attacker could simply set their own public key to replace that of a Keeper, and then they would have the right to execute a high volume transaction within the Poly network to exfiltrate a large amount of funds to other wallets. Clearly not something you want to happen.
Now, about EthCrossChainManager. This is another high privilege contract that has the right to trigger messages from another chain to the Poly chain. Which means: anybody can call a cross-chain event by issuing a transaction on the source chain that invokes the verifyHeaderAndExecuteTx function within EthCrossChainManager, and specifying a target Poly contract to execute. However, because of its particular intended use, EthCrossChainManager would not call any function within the target contract, but only the one with a very specific “Solidity function ID”. Namely, it would only call a function whose 32-bit ID is computed this way:

In other words, the ID of the function called is computed as the 32-bit truncation of a 256-bit Keccak hash of the string _method and a suffix.
The attacker exploited two problems here.
The first one is, it turns out that EthCrossChainManager is an owner of EthCrossChainData, and can therefore execute privileged functions within this one! The second one is that the field _method in the code snippet above is actually user-defined, and can therefore be set at will. In particular, it is possible for the attacker to brute-force a _method field that hashes to a 32-bit value that is exactly the ID for putCurEpochConPubKeyBytes!
This is the attack in detail:
ethers.utils.id ('putCurEpochConPubKeyBytes(bytes)').slice(0, 10)'0x41973cd9' ethers.utils.id ('f1121318093(bytes,bytes,uint64)').slice(0, 10)'0x41973cd9' A total worth of over 610 million USD was stolen this way. That’s a lot.
Aftermath
Things are moving fast and it is difficult to keep track in real time of the evolving situation.
After the hack, the next reasonable step would have been for the attacker to transfer the stolen funds into the liquidity pool of an anonymous decentralized exchange like Curve. For this reason, Poly immediately issued a request for crypto miners and exchanges to “blacklist” the stolen funds, making them de facto unavailable to the attacker. This is in theory possible to do, but cumbersome for the exchanges for many reasons, and it is unclear whether all exchange platforms answered the plea.
However, administrators of the stablecoin Tether (who have greater control over their blockchain compared to other DeFi applications) managed to freeze the stolen Tether-USDC funds in time just 9 blocks before the attacker tried to launder them on the Curve liquidity pool. An anonymous user alerted the hacker by issuing an Ethereum transaction to the hacker’s address with the freeze warning message in the metadata, and the hacker rewarded this user with part of the stolen Ethereum. After that, the address of the hacker was flooded with transactions of hundreds of people begging for money.
Poly Network asked the hacker to return the funds. The security company Slowmist published findings on the alleged hacker, claiming that the hacker’s identity had been exposed and that the group had access to the hacker’s email and IP address. According to Slowmist, the hacker was able to take advantage of a relatively unknown crypto exchange in Asia and they claimed to have a lot of information about the attacker.
Whether this is true or not, the hacker started returning funds to Poly on Wednesday. By August 11th 15:00 UTC nearly half worth of tokens have been returned, and the hacker claims to be ready to return more in exchange for the unfreeze of the Tether tokens. A second message embedded in a transaction reads: “IT’S ALREADY A LEGEND TO WIN SO MUCH FORTUNE. IT WILL BE AN ETERNAL LEGEND TO SAVE THE WORLD. I MADE THE DECISION, NO MORE DAO”.
While this story develops, it is not superfluous to remind that “blockchain” is not synonymous with “security”. It is very important to audit the security of your applications, including smart contracts.
We continue our post-quantum series with this blog post that details the process behind adding quantum resistance to the WireGuard protocol and evaluating the performance of the resulting software.
WireGuard is a fast and secure open source virtual private network (VPN) solution that is using state-of-the-art cryptography. While being extremely fast, the WireGuard VPN secures your connection and protects your online privacy with several nice security features such as data security or identity-hiding.
The WireGuard protocol works in two steps, first a handshake is performed between client and server. During this handshake, both participants authenticate both to each other and at the same time securely exchange some key material. The derived key material is used to open a secure tunnel using symmetric key encryption and exchange the actual network data. When using this tunnel a client can then “hide” behind the server and obtain additional privacy from other parties while surfing the web.

The problem here is that the cryptography in use, as modern as it is, is not quantum resistant, thus threatened by the arrival of quantum computers.
As it relies on symmetric encryption, the tunnel part can be made quantum secure by “simply” doubling the key size. We thus put our main focus on the more complex challenge of adding quantum resistance to the WireGuard handshake.
There are already some works that propose post quantum handshakes for VPN. In the table below, we report the results of works published last year that add quantum resistance to respectively the OpenVPN protocol, or the WireGuard protocol. As we can see, the proposed quantum resistant handshake of WireGuard is much more practical than the one of OpenVPN. And this is why we chose to continue working with WireGuard specifically, and use these results as baseline.
Before we dive into the technical details, it’s important to just step back and think about what we want to achieve and what we want to evaluate. The performance of a VPN handshake has two dominant dimensions: the runtime and the bandwidth. Both are important but one can prevail according to the setting we are in. In some cases, a lower runtime is preferred either for convenience or to fit computationally-limited resources. On the opposite, some might require solutions where the bandwidth is the feature to minimize.
It’s important for every scenario to define the settings and constraints in order to propose the best tool that there is to maximize the performance. For instance, discarding the importance of bandwidth usage at the benefit of the runtime is prone to fire back as the network limitations may cause considerable delay, leading to a slow and heavy protocol.
With this in mind, the first thing that we do is to repeat the methodology of the prior PQ-WireGuard. We build our implementations on top of the Go mirror implementation of WireGuard.
The PQ-WireGuard handshake is built around an authenticated key exchange. In this case the authenticated key exchange follows the Fujioka construction –included below–, a recipe that combines two Key Encapsulation Mechanisms (KEM) to obtain the authentication, integrity and confidentiality properties. One of the KEM is working with static keys and is required to be CCA secure, and the second one is working with ephemeral keys, and is required to be only CPA secure. Only participants holding the correct private key can learn the internal view of the protocol and derive the correct key material, while the others can only at best guess at random.

We use plain Kyber as our KEM instances, with a security level 3 (Kyber768). Kyber is a really fast finalist that is built using a security-enhancing transform over a weakly-secure encryption scheme. We use our own open-source implementation, introduced in our previous blog post.
This simple approach yields results that are not very competitive on the bandwidth side, but promising when looking at the runtime, especially on the client side.
Performance of post-quantum handshakes.
We examine further our solution to try to find out “how fast can we go”?
To do such, we tweak the Kyber KEM instance working with ephemeral keys. We start by removing the Fujioka transform, and build our CPA-secure Kyber KEM directly on top of the encryption scheme. We also augment the compression of the ciphertexts. We can do that because in practice, a negligible decapsulation failure rate can be an overkill compared for example to the possibility of a packet drop.
These tweaks reduce the size of outputs, boost the performance, and actually increase the security, with arguments that can be borrowed from learning with rounding schemes. Because of potential attacks, we cannot apply this trick to the Kyber instance working with static keys. Indeed there exist attacks targeting the decapsulation failures that could lead to a secret key recovery, and of course we do not want that. The internals of the tweaked instance are detailed below.
Tweaked Kyber internals.
As a result, for security somewhat equal we obtain a protocol that offers improvement over the prior works regarding computational time while staying competitive regarding the bandwidth. Indeed, the handshake occurs every 2 minutes or so, the 2 extra IP packets being sent thus do not impact the practicality. As expected, the bigger improvement is on the client side, where we almost half the runtime compared to the prior PQ-WireGuard.
Performance of post-quantum handshakes.
Whereas the lower bound of one IP packet per message is reached for prior PQ-WireGuard handshake, we quickly come to the conclusion that there is no way, as aggressively as we tried, to fit our Kyber-based handshake into two IP packets only. Either the security level drops, or the failure rates drop below an acceptable rate. We identify the bottleneck in our case as being the size of the ciphertexts of the CCA-secure Kyber scheme.
We deviate from the original approach to try to now optimize the communication costs.
We study the use of the del Pino construction, a different authenticated key exchange recipe that replaces the KEM working with long term authenticating keys by a signature scheme.

We keep using our tweaked Kyber instance with ephemeral keys, and choose to work with Rainbow, a signature scheme that stands out because of its very small signature size. The relatively large public key size of rainbow is not a problem in our setup as after a one time transfer, only a hash can be used during the frequent handshakes. Hence this preprocessing is amortized pretty quickly.
Performance of post-quantum handshakes.
As you can see, we achieve a very light protocol. With this approach, saving of almost 2 thousand bytes –hence two IP packets — comes at the cost of some few milliseconds of computational overhead, a cost that is barely perceptible by the user.
The security properties of the original WireGuard protocol are maintained under the assumptions that the users public keys are not known to the attacker. We agree with the arguments of Hülsing et al. in Post-Quantum WireGuard and Appelbaum et al. in Tiny WireGuard tweak that conclude that such assumption is realistic. Then, as only a hash is used during the handshakes, any attacker such as eavesdroppers cannot threaten the deniability and anonymity properties.
Our quantum-resistant WireGuard fork including all the aforementioned PQ-WireGuard variants is publicly available at https://github.com/kudelskisecurity/pq-wireguard. Furthermore, knowing that quantum computers are currently at the stage of prototypes, we choose in our final product to use instances with a light security level, i.e., Kyber512, which offers security roughly equivalent to AES–128, and Rainbow-1. As result, our quantum-resistant WireGuard implementations achieve an even better performance, as reported on the table below.
Performance of the post-quantum handshakes included in our WireGuard fork.
When used correctly, a VPN is an useful tool whose security should be assured even after the advent of quantum computers. Towards that goal, we presented two performant quantum resistant WireGuard constructions. We show that there is still some improvement surface, especially when looking to optimize one dimension especially among bandwidth or runtime, while staying practical in the other one.
We agree that prior PQ-WireGuard looks like a very good compromise, but we are pleased to present protocols that show some improvements in different practical settings, and that provide experimental results that can be used as motivation to accelerate the transition towards post quantum alternatives.
If you want to use our PQ-WireGuard protocols, the tutorial is included in the README of our GitHub repository. For additional information, you can also watch the recording of our presentation at the NIST third post-quantum cryptography standardization conference.
As last word, we would like to mention that we plan to integrate recent optimizations and port our implementation to the Linux kernel space, hopefully to get even better runtimes. Stay tuned!
Today we are excited to release oramfs, a simple, flexible, Free Software ORAM implementation for Linux written in Rust. It is designed to support different ORAM schemes and encryption ciphers. It is storage- and cloud provider agnostic and supports any UNIX filesystem without requiring specific plugins or server-side support.
When looking at privacy of storage solutions, encryption alone is not sufficient to prevent access pattern leakage. Unlike traditional solutions such as LUKS or Bitlocker, an ORAM scheme prevents an attacker from knowing whether read or write operations are performed and which parts of the filesystem are accessed. This level of privacy is achieved by making extra access requests than necessary, shuffling the blocks composing the storage layer, and writing and re-encrypting data back and forth every time, even when just a read operation is performed. This obviously comes with a loss of performance, but brings additional security compared to other solutions.
With oramfs users can enjoy total privacy while storing their files on untrusted local or remote storage, such as a public cloud. Our solution is resizable, so there is no need to re-initialize a larger ORAM when space becomes a problem. With oramfs, a whole filesystem is encrypted and read/write access patterns are hidden from the remote server. Our initial release supports a simple implementation of Path ORAM, one of the first modern, tree-based ORAM schemes, but the design supports arbitrary schemes and we plan of releasing more optimized ones soon.
To setup the ORAM, two inputs are required: a public directory and a private directory.
The public directory can be any locally mounted directory, including remote volumes mounted as a local directory. This could be a remote FTP directory, or a remote cloud storage mounted locally. Just mount the remote storage (or use a local storage if you want) and you’re good to go. For example, Rclone supports mounting a variety of cloud storage providers as local directories.
The private directory is the local mount point for a virtual storage that is presented to the user after entering the right secret passphrase, and is used to access files stored in the ORAM. Any operation performed on the private directory has an impact on the public directory. And if that public directory is a mounted remote storage, then it is safely and transparently synchronized to the remote server whenever an operation is performed on the private directory.
In order to be fully storage agnostic, oramfs is file-based: the ORAM structure is split into blocks (or nodes of blocks) and every block or node is saved to the public directory as a separate file. FUSE is then used to recombine these files into a single virtual loop device that is eventually accessed through the ORAM scheme itself. All this is completely transparent for the user.
Let’s go through an example where we setup an ORAM that transparently synchronizes data to a remote FTP server. This would also work with any other remote storage supported by Rclone, such as SSH, Google Drive or S3.
We assume that a remote directory has already been mounted at the local mount point public/ and also an empty directory private/ has been created.
We initialize a new ORAM called myoram, by answering the interactive questions. This will automatically format the underlying filesystem with EXT4 but this option can be overridden if wished.
$ oramfs add myoram public/ private/
Please enter desired ORAM total size in bytes,
or press enter to use default [default: 16000000 (16 MB)]:
Adjusting ORAM size to closest valid value: 16711680 bytes
Please enter path to client data directory to use, or press enter to use default [default: /home/foobar/.config/oramfs/myoram]:
Please enter path to mountpoint directory to use, or press enter to use default [default: /tmp/oramfs_myoram]:
Successfully added ORAM myoram.Please enter path to mountpoint directory to use, or press enter to use default [default: /tmp/oramfs_myoram]:
Successfully added ORAM myoram.
Finally, we mount the ORAM (will prompt the user for the password) and write a file to it:
$ oramfs mount myoram
It looks like this ORAM is mounting for the first time. Initializing it first...
Please enter an encryption passphrase to secure your ORAM: ****
Please type it a second time to confirm: ****
Starting ORAM...
Running as a daemon...
Mounting filesystem to private directory...
Formatting EXT4 filesystem...
mke2fs 1.46.2 (28-Feb-2021)
Creating filesystem with 16320 1k blocks and 4080 inodes
Filesystem UUID: 7de4f86e-adb8-4587-bf68-814267ef5ab6
Superblock backups stored on blocks:
8193
Allocating group tables: done
Writing inode tables: done
Creating journal (1024 blocks): done
Writing superblocks and filesystem accounting information: done
Mounting directory...
[sudo] password for foobar:
Setting private directory owner...
[sudo] password for foobar:
Setup complete! ORAM filesystem mounted to private directory at: /home/foobar/git/oramfs/private
$ echo hello world > private/somefileWhen finished, we can unmount it:
$ oramfs umount myoramThat’s it! Files written/read to/from the private directory are encrypted and access patterns are hidden from the FTP server.
Head over to Github to get started with oramfs.
By the way, we will be presenting oramfs on Wednesday July 7th at 4:10pm CEST at Pass the SALT.
When Kudelski Security approaches a security assessment of a FinTech company such as Oxygen, we always aim to provide a full suite of coverage to ensure that the architecture, application, and operation of the environment is sound and free from known vulnerabilities or DeFI style attacks.
In the case of a DeFI ecosystem, there are always moving parts of dependencies, relationships, and workflows that may be dynamic. You may not always know the workflow, even if you know the components. In this manner, it is important to always understand the picture of the entire system, review the flow, understand the purpose and functionality and then view the system from the point of view as an attacker.
As a philosophy, a protocol such as Oxygen – in addition to providing the financial functions that it has been designed to provide – must protect its components from impact/liquidation even if one of the dependent components introduces a failure or economic scenario that is not intended.
While not every security risk can be uncovered by a single review, the artifacts that are created can be used as input for the organization to build security risk and operational controls into its system to better handle unintended system interactions. Whether it be economic or system level attacks, the security of the system takes review, continuous testing, analysis from experts in the form of pen testing or bug bounty, and review by legal and economic experts.
Within this lens, we are undertaking a series of assessments related to the Oxygen Protocol and supporting architecture and openly discussing the methodology to drive understanding further into the industry. We will conduct a series of reviews and audits of the Oxygen Protocol functionality and interfaces, as well as collaborating with Oxygen going forward to assess future enhancements, changes, or additions to Oxygen’s protocols.
As they state, beginning with prime brokerage, Oxygen intends to replicate a growing range of products offered by investment banks, helping to build finance without Wall Street. As a DeFi ecosystem, Oxygen intends to enable delivery of these services to individuals and institutions alike, democratizing access to sophisticated investment, risk management and liquidity solutions.
As a security provider, understanding the outcome desired by Oxygen drives our review criteria and also identifies which sorts of fraud, risk, and security controls that must be built into this sort of system. Traditional entities are built upon years of security learnings and audits, and any project intending to replicate this functionality should also deliver equivalent functional controls.
These controls deliver protections such as:
Security projects utilizing a STRIDE methodology along with appropriate control points and attack trees generally can understand and prioritize their risk surface, similar to how Oxygen is progressing.
As a protocol, Oxygen is built on the growing and liquid Serum ecosystem which leverages an on-chain orderbook to match borrowers and lenders to provide fair rates. And it is made possible by the massive throughput and ultra-low costs of the scalable Solana blockchain. Kudelski Security has previously performed assessments for Solana and has compiled a repository of knowledge which will be leveraged to better understand and review the Oxygen system.
In a follow-up article, we will discuss our process and outcomes from the review of the Oxygen ecosystem. Much can be learned from the attention on the Contracts Mechanism, Finance Logic, Yields, Borrowing, Lending and Leverage mechanism security controls and constraints, as well as other related funds safety considerations – such as whale attacks and collusion.
We will also discuss how “formal verification analyses” can confirm that formulas, cryptography and mathematics, etc. in the software faithfully implement the specified intent of the protocol.
Our final update will include the methodology of testing, which includes a discussion of the benefits of architecture review, dynamic testing of key financial risk scenarios such as edge situations, liquidity events, and more versus scripted testing or a pure static analysis.
More to come…
Concordium is a science-based proof-of-stake blockchain created with business applications in mind. It is the first blockchain with identification built into the protocol to meet regulatory requirements, while delivering a user-friendly platform that can handle smart contracts.
The Concordium Platform is designed to be fast, secure and cost-effective, which is why Concordium and Kudelski Security have been working together to perform threat modeling, code review, and “scenario based” assessment of the underlying code and logic of the Concordium software.
As documented in the White Paper, Concordium implements a Network Layer, Consensus Layer, and Identity Layer. These three layers interact with each other to process transactions and to execute the scalable/secure transactions.
At the beginning of the engagement, we first wanted to focus on the critical parts of the code which ensured that the foundation was solid, and that consensus and execution remained safe. We focused much of the security review on the following components and their interconnection.
During discussions with the core team, we quickly identified some key areas of overall concern
We ruled the following components out of scope for this first engagement
Following the review, we have come to these conclusions which we have presented to the project team as part of the report output
During the threat modeling exercise, we worked collaboratively with the Concordium team to identify the most important threats to the project. We used this information to guide the code review phase of the project.
During the code review, we found no critical or high-risk scenarios that put the tokens of the system at risk and we made a number of suggestions to improve overall documentation, code quality, and conformance to best practices.
Following the completed review, and during follow-on tests, scenarios were discovered in which unused network messages and test code could cause availability concerns on the network even though these errors would not cause a loss of funds. We completed a follow-on review, looking specifically at the network code and identified a few issues that the Concordium team fixed.
We look forward to continuing the effort with Concordium to bring trust into their ecosystem through diligent review of the critical components of their ecosystem
Heard about the quantum threat glooming on the horizon? Today, we dive into post-quantum, or quantum-resistant crypto, which is not to be mixed with quantum cryptography (see our previous blog entry on that), and unbox our post-quantum Go library.
The arrival of quantum computers threaten the security of traditional cryptographic algorithms. Shor’s, Grover’s and Brassard’s algorithms are few examples of tools that enable powerful quantum attacks on the public key cryptography as we know and use it. The hardness of some schemes such as RSA or Diffie-Hellman is not granted anymore, prompting us to find replacements that rely on alternative assumptions, namely post-quantum cryptography.
Lattice-based constructions make for the majority of post-quantum schemes. A lattice is a set with an order relation, meaning that every two elements have unique supremum and infimum. The most important lattice-based computational problem is the Shortest Vector Problem (SVP), which entails finding the shortest vector in a given lattice. The security of most of the lattice-based constructions can be reduced to the security of the SVP problem, which appears to be resistant to attacks by both classical and quantum computers.
The CRYSTALS algorithms are promising candidates for the post-quantum area, and are finalists of the NIST post-quantum cryptography standardization process. They stand out because of their great performance, the main drawback being their relatively large outputs size.
The first protocol of the CRYSTALS suite is Kyber, a Key Encapsulation Mechanism (KEM), that secures the exchange of key material over an insecure channel. It uses asymmetric encryption to transmit a short symmetric key. This symmetric key is then used to encrypt the longer messages. Briefly, Kyber is built using a security transformation that turns Regev’s weakly secure encryption scheme into a strongly secure one.
The second protocol is Dilithium, a Digital Signature Algorithm (DSA), that produces a signature on a message that is verifiable by anyone holding the public verification key associated to the secret signing key. Dilithium follows a Fiat-Shamir construction, sampling a mask and rejecting until the signature is binding yet hides the signing key.
The security of those protocols is parametrized by various factors such as the size of the lattice. Kyber and Dilithium can be instantiated to offer a light, recommended, or very high security level, and are respectively designated as Kyber512, Kyber768, and Kyber1024, or Dilithium2, Dilithium3, and Dilithium5.
A thorough cryptanalysis is necessary but not sufficient to guarantee the security of a library. The last decade has seen the number of publications about a new class of attacks arise (Meltdown, SPECTRE, Zombiland…). Side-channels attacks target the implementation of algorithmically secure crypto systems and can exploit information gained from the implementation itself.
Often overlooked, side-channel attacks can be used to weaken or break entirely the security of an infrastructure. Not paying enough attention to side-channel vulnerabilities can lead to devastating breaches, as we have seen with NSA’s TEMPEST system that could reconstruct the entirety of a computer’s screen by listening to its electromagnetic emissions, or more recently with the SPECTRE attacks, that can manipulate a process into revealing sensitive data, such as passwords, after monitoring the cache hits and misses timing. This is why we make it a priority to offer as much protection against said side-channel attacks as possible with our implementation.
The Go language has gained popularity amongst cryptographers because of its simplicity and security properties. Writing and maintaining Go code is easy and a great fit for projects of all scale, from proof-of-concepts to complete infrastructures.
In our library, we offer an implementation in Go of the CRYSTALS protocols. The open-source code is available in the crystals-go repository. While there are existing implementations that consist of a straightforward translation from the official implementations available in C to Go, we made the choice to deviate from the reference to provide additional security and performance properties. Correct behavior of our code is ensured and tested via known-answer tests provided by the CRYSTALS’s authors.
In this section, we provide hands-on examples on how to use our library. First, the crystal-go module can be installed via:
go get -u github.com/kudelskisecurity/crystals-goIn order to have a clear separation between the parameters and the algorithms, we define generic methods that work with all possible sets of parameters, and invoke them on an instance of Kyber or Dilithium that completely defines the parameters to be used. Hence, a user first has to define which level of security they want to work with by creating an instance of Kyber or Dilithium among Kyber512, Kyber768, Kyber1024 and Dilithium2, Dilithium3, Dilithium5:
k := NewKyber512() //Creates a Kyber instance with light security level
d := Dilithium3() //Creates a Dilithium instance with recommended security levelThe user can now generate a public and private key pair by calling:
pk, sk := k.KeyGen(seed)or
pk, sk := d.KeyGen(seed)Once the parameters are defined and the keys are generated, the core functionalities of Kyber and Dilithium can be called.
Kyber’s main functions are Encaps and Decaps. A typical flow of Kyber is:
//Select the parameters
k := NewKyber512()
//Generate the keys and openly disclose the public key.
pk, sk := k.KeyGen(seed)
//Generate a shared secret and a ciphertext. Similarly to the seed, the random
//coins used during the encapsulation can be given or deleguated to the method.
c, ss := k.Encaps(coins, pk)
//Send the ciphertext to the private key holder, which decapsulate it to recover
//the shared secret (ss = ss').
ss' := k.Decaps(c, sk)Similarly for Dilithium, the main methods Sign and Verify are typically used as follows:
d3 := Dilithium3() //Creates a Dilithium instance with recommended security level
pk, sk := d3.KeyGen()
msg := []byte("This is a message.")
sig := d3.Sign(sk, msg)
verified := d3.Verify(pk, sig, msg) //The boolean verified is true for honest runsA seed and coins of 32 bytes can be given as argument and allows for reproducibility for example, or is useful if the user does not trust the environment to generate good randomness and wants to use randomness from their own source.
They can be nil in which case the randomness will be generated during the key generation process using Go’s official crypto/rand library.
This section is not exhaustive of the features offered by our library and we refer to our README for a more complete description.
Our library stands out because of its security properties. Among the vulnerabilities reported on the original implementation, we integrate countermeasures for most of them, providing a library that is both theoretically and practically secure. We predict that new attacks will be published as the candidates are refined, and expect changes in the code to occur as the security of our library is treated as a continuous process.
We recall that side-channel attacks are high-risk threats and encourage users to prefer libraries with strong implementation security, such as our library, over implementations that lack these guarantees.
In order to provide an insight on the computation and communication costs of our library, we report in the following table the runtime in milliseconds and the outputs size in bytes of our implementation for a recommended security level.
Performance overview for a recommended security level
The relatively large outputs size can impact some applications but is never considered a bottleneck. Furthermore, we show in the following graphs that the performance of current classical security public-key and signature schemes (ECDSA and RSA-OAEP) can be excelled by our implementation. This difference can be explained by the very simple arithmetic the CRYSTALS suite relies on.


Our post-quantum library is easy to use, fast and secure. The practicality of our library should be used as motivation to integrate post-quantum alternatives in your infrastructures, to protect your data well into the foreseeable future without compromising on the performance.
At Kudelski Security, we perform quite a few security and cryptography reviews involving Rust code. Rust support in tooling has been lacking. We’ve developed some tools internally to assist in our reviews, but we were looking for a more general and mature framework that supports multiple languages. Semgrep is great great tool for performing static analysis and its support for multiple programming languages makes it valuable for developers and security professionals alike. Experimental support for Rust was recently added to Semgrep and we will see how to use it by going through a quick example, which will demonstrate some limitations. Then we’ll see how we improved this Rust support increasing usability for the scenarios we and many others find ourselves in everyday.
First we install Semgrep from PyPi:
$ pip install semgrepThen, we create a file named rules.yml with the following contents. This defines a very simple rule that matches those exact two lines defining a variable x and a variable y. We could of course define more complex rules, but this is not the point here:
rules:
- id: my-rule-id
patterns:
- pattern: |
let x = 0;
let y = x * 2;
message: "Found a match for my custom rule."
languages: [ rust ]
severity: WARNINGAfter that, we clone our codebase and run Semgrep on that directory using our custom rules. Semgrep then tells us which parts of the codebase match those rules.
$ git clone [email protected]:ing-bank/threshold-signatures.git
$ cd threshold-signatures
$ semgrep --config rules.yml .
running 1 rules...
semgrep error: invalid pattern
--> rules.yml:4
4 | - pattern: |
5 | let x = 0;
6 | let y = x * 2;
7 | message: "Found a match for my custom rule."
Pattern could not be parsed as a Rust semgrep patternBut this is not working!? Well, it appears that the two lines of Rust code in the semgrep pattern are not valid Rust code by themselves. If you try to compile a Rust source file containing only those two lines, it won’t work.
$ cat test.rs
let x = 0;
let y = x * 2;
$ rustc test.rs
error: expected item, found keyword `let`
--> test.rs:1:1
|
1 | let x = 0;
| ^^^ expected item
error: aborting due to previous errorIndeed, the Rust grammar is defined in a way that this should be wrapped in a function definition to be valid:
$ cat test.rs
fn main() {
let x = 0;
let y = x * 2;
}
$ rustc test.rs
warning: unused variable: `y`
--> test.rs:3:7
|
3 | let y = x * 2;
| ^ help: if this is intentional, prefix it with an underscore: `_y`
|
= note: `#[warn(unused_variables)]` on by default
warning: 1 warning emittedObviously, this produces a warning, but it now compiles.
To parse the pattern defined in our rule, semgrep uses tree-sitter, a parser generator, and more specifically, a modified version of the tree-sitter-rust grammar in the case of Rust source code. Similarly to the Rust compiler, this grammar won’t accept these two lines just by themselves. So we need to update the pattern in our rule:
rules:
- id: my-rule-id
patterns:
- pattern: |
fn $F(...) {
let x = 0;
let y = x * 2;
}
message: "Found a match for my custom rule."
languages: [ rust ]
severity: WARNINGNote that we used some special semgrep pattern syntax here: metavariables ($F) and the ellipsis operator (…). Indeed, Semgrep extends the tree-sitter-rust grammar to support additional language features only for semgrep patterns. This lets us match any function with any parameters without having to hardcode that for every possible case.
Now, if we run Semgrep again with our updated rule, we get some matches in our test.rs file:
$ semgrep --config rules.yml .
running 1 rules...
test.rs
severity:warning rule:my-rule-id: Found a match for my custom rule.
1:fn main() {
2: let x = 0;
3: let y = x * 2;
4:}
ran 1 rules on 20 files: 1 findings
2 files could not be analyzed; run with --verbose for details or run with --strict to exit non-This would be less painful if we could write only those two lines as the pattern for our rule. Additionally, the whole function is being printed out, but we only wanted to search for those two lines. For long functions, this makes it difficult to see which line matched.
Well, this is now possible thanks to a contribution we made to Semgrep:
We extended the tree-sitter-rust grammar to support a list of statements directly as a pattern and we updated the CST to AST mapping to make it work. So it is now possible to write rules directly containing a list of statements:
rules:
- id: my-rule-id
patterns:
- pattern: |
let x = 0;
let y = x * 2;
message: "Found a match for my custom rule."
languages: [ rust ]
severity: WARNINGSemgrep now accepts that pattern and only prints the lines we wanted to match:
$ semgrep --config rules.yml .
running 1 rules...
test.rs
severity:warning rule:my-rule-id: Found a match for my custom rule.
2: let x = 0;
3: let y = x * 2;
ran 1 rules on 20 files: 1 findings
2 files could not be analyzed; run with --verbose for details or run with --strict to exit non-zero if any file cannot be analyzedNote: Since these changes were recently merged, they will only appear in the next semgrep release. You will have to build from source to see them immediately.
We love being able to write rules more easily now and hope this will be helpful to others too. Our hope is that this expanded support leads to quicker identification of bugs and security issues in Rust code. Happy bug hunting.