Skip to content

Commit

Permalink
side_channels: Add SLH DSA report
Browse files Browse the repository at this point in the history
  • Loading branch information
aewag committed Oct 11, 2024
1 parent 572e000 commit a1ce11e
Show file tree
Hide file tree
Showing 3 changed files with 214 additions and 0 deletions.
6 changes: 6 additions & 0 deletions docs/audit_report/src/06_bibliography.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,9 @@
.. [BOTAN_LMS_PROCESS] https://github.com/randombit/botan/blob/1900c9e1f021b395dce2baba868a6427c1690ff1/src/lib/pubkey/hss_lms/lm_ots.cpp#L31
.. [BOTAN_LMS_Q_WITH_CKSM] https://github.com/randombit/botan/blob/1900c9e1f021b395dce2baba868a6427c1690ff1/src/lib/pubkey/hss_lms/lm_ots.cpp#L80
.. [BOTAN_SLH_DSA_TREEHASH] https://github.com/randombit/botan/blob/7353a40e7ee1a0b845a798d94f8d88110e79347a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_treehash.cpp#L64
.. [BOTAN_SLH_DSA_WOTS_SIGN_AND_PKGEN_SIG_NODE] https://github.com/randombit/botan/blob/7353a40e7ee1a0b845a798d94f8d88110e79347a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_wots.cpp#L159
.. [BOTAN_SLH_DSA_WOTS_SIGN_AND_PKGEN_SIG_NODE_HC] https://github.com/randombit/botan/blob/7353a40e7ee1a0b845a798d94f8d88110e79347a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_wots.cpp#L180
1 change: 1 addition & 0 deletions docs/audit_report/src/side_channels/01_00_results.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ The descriptions usually also include the associated source code and, if applica
.. toctree::

01_01_lms
01_04_slh_dsa
207 changes: 207 additions & 0 deletions docs/audit_report/src/side_channels/01_04_slh_dsa.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
"""""
SLH DSA
"""""

Analysed variants:

- SphincsPlus-sha2-128s-r3.1, deterministic

For the analysis of SLH DSA, a utility was written that calls the functions to be analysed in a similar way to the Botan CLI.
The following call is used to generate a signature:

.. code-block:: cpp
auto params = Botan::Sphincs_Parameters::create("SphincsPlus-sha2-128s-r3.1");
Botan::SphincsPlus_PrivateKey priv_key(params.algorithm_identifier(), priv_key_bits);
Botan::PK_Signer sig(priv_key, rng, "Deterministic");
signature = sig.sign_message(message, rng);
The Botan library is configured using the following console prompt:

.. code-block::
./configure.py --prefix=~/workspace/bsi/DATA/cryptolib/botan/build --cc=gcc \
--cc-bin=g++-12 --cc-abi=-fno-plt --disable-modules sm4 --disable-sse2 \
--disable-ssse3 --disable-sse4.1 --disable-sse4.2 --disable-avx2 \
--disable-bmi2 --disable-rdrand --disable-rdseed --disable-aes-ni \
--disable-sha-ni --disable-altivec --disable-neon --disable-armv8crypto \
--disable-powercrypto --without-os-feature=threads --with-debug-info
The binary is compiled with the `gcc` compiler with the following version:

.. code-block::
$ g++-12 --version
g++-12 (Debian 12.2.0-14) 12.2.0
The host operating system is `Debian GNU/Linux 12 (bookworm)`.

The SLH DSA standard uses a variant of XMSS within the hypertree and refers to it as XMSS for short.
In line with the standard, this procedure is also referred to as XMSS in this section.

**Modification of the hypertree**

The runtime for generating a signature depends on the height of the hypertree and the number of Merkle trees.
To reduce the runtime, the total height is limited to four and two Merkle trees are used, each with a height of two.
This change has no influence on the code coverage of the side channel analysis performed.

.. code-block::
--- a/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.cpp
+++ b/src/lib/pubkey/sphincsplus/sphincsplus_common/sp_parameters.cpp
@@ -230,7 +230,7 @@ Sphincs_Parameters Sphincs_Parameters::create(Sphincs_Parameter_Set set, Sphincs
switch(set) {
case Sphincs_Parameter_Set::Sphincs128Small:
case Sphincs_Parameter_Set::SLHDSA128Small:
- return Sphincs_Parameters(set, hash, 16, 63, 7, 12, 14, 16, 133);
+ return Sphincs_Parameters(set, hash, 16, 4, 2, 12, 14, 16, 133);
case Sphincs_Parameter_Set::Sphincs128Fast:
case Sphincs_Parameter_Set::SLHDSA128Fast:
return Sphincs_Parameters(set, hash, 16, 66, 22, 6, 33, 16, 128);
**Summary**

DATA did not detect any leaks in the analysed SPHINCS+ implementation.
In phase one DATA only identified differences in programme execution.
In phase two, however, no statistical dependency on the secret cryptographic keys used and the difference of execution can be detected.

The cause for the differences in execution are explained in the following sections.
Overall, the analysis shows five differences during the execution.
One difference was found within the FORS method, the remaining four differences concern the XMSS method.


**Difference in execution: FORS - treehash**

The FORS method is based on the use of Merkle trees.
The public FORS key is the root node in a Merkle tree.
The children of this node are root nodes of individual Merkle trees that are used to sign a message.

The `treehash` routine uses the leaves of a Merkle tree to calculate the nodes above it.
Components of a FORS signature are the so-called authentication data.
These are nodes that are generated during signature generation and are required for verification in order to calculate the respective root of the Merkle tree.During verification, parts of the leaves are calculated depending on the message and the signature.
The remaining nodes required to calculate the root are the so-called authentication data, which are also contained in the signature.

The `treehash` routine detects during execution whether the currently calculated node must be added to the authentication data [BOTAN_SLH_DSA_TREEHASH]_.
If this is the case, a condition in the programme flow is fulfilled and the programme execution is changed.
This control flow difference is indicated by DATA.
The difference is not critical because the values of the nodes within these Merkle trees are public.
Consequently, it is also uncritical if the differences indicate which nodes belong to the authentication data.
This knowledge can also be derived from a message and the associated signature.

.. code-block:: cpp
void treehash(StrongSpan<SphincsTreeNode> out_root,
StrongSpan<SphincsAuthenticationPath> out_auth_path,
const Sphincs_Parameters& params,
Sphincs_Hash_Functions& hashes,
std::optional<TreeNodeIndex> leaf_idx,
uint32_t idx_offset,
uint32_t total_tree_height,
const GenerateLeafFunction& gen_leaf,
Sphincs_Address& tree_address) {
[...]
// Check if the node we have is a part of the authentication path; if
// it is, write it out. The XOR sum of both nodes (at internal_idx and internal_leaf)
// is 1 iff they have the same parent node in the FORS tree
if(internal_leaf.has_value() && (internal_idx ^ internal_leaf.value()) == 0x01U) {
auto auth_path_location = out_auth_path.get().subspan(h.get() * params.n(), params.n());
copy_mem(auth_path_location, current_node);
}
[...]
}
**Difference in execution: WOTS - treehash**

The XMSS method is based on the WOTS method and the use of Merkle trees.
Similar to the FORS method, the XMSS method also uses the `treehash` routine.
Here too, there is a similar difference in programme execution when adding individual nodes to the authentication data of a signature [BOTAN_SLH_DSA_TREEHASH]_.
As with the FORS method, this difference is also uncritical with the XMSS method.

.. code-block:: cpp
void treehash(StrongSpan<SphincsTreeNode> out_root,
StrongSpan<SphincsAuthenticationPath> out_auth_path,
const Sphincs_Parameters& params,
Sphincs_Hash_Functions& hashes,
std::optional<TreeNodeIndex> leaf_idx,
uint32_t idx_offset,
uint32_t total_tree_height,
const GenerateLeafFunction& gen_leaf,
Sphincs_Address& tree_address) {
[...]
// Check if the node we have is a part of the authentication path; if
// it is, write it out. The XOR sum of both nodes (at internal_idx and internal_leaf)
// is 1 iff they have the same parent node in the FORS tree
if(internal_leaf.has_value() && (internal_idx ^ internal_leaf.value()) == 0x01U) {
auto auth_path_location = out_auth_path.get().subspan(h.get() * params.n(), params.n());
copy_mem(auth_path_location, current_node);
}
[...]
}
**Execution difference: WOTS - wots_sign_and_pkgen**

In addition to the differences in the `treehash` routine, three differences are also detected in the `wots_sign_and_pkgen` function.
This function generates the signature data for the WOTS procedure and the public WOTS keys for the other leaves in the Merkle tree.

The first difference is a control flow difference.
The implementation distinguishes whether signature data must be created for the WOTS procedure or whether only the public WOTS key is required [BOTAN_SLH_DSA_WOTS_SIGN_AND_PKGEN_SIG_NODE]_.
This information can also be calculated using the message and the associated signature, which classifies the difference as non-critical.

.. code-block:: cpp
void wots_sign_and_pkgen(StrongSpan<WotsSignature> sig_out,
StrongSpan<SphincsTreeNode> leaf_out,
const SphincsSecretSeed& secret_seed,
TreeNodeIndex leaf_idx,
std::optional<TreeNodeIndex> sign_leaf_idx,
const std::vector<WotsHashIndex>& wots_steps,
Sphincs_Address& leaf_addr,
Sphincs_Address& pk_addr,
const Sphincs_Parameters& params,
Sphincs_Hash_Functions& hashes) {
[...]
for(WotsChainIndex i(0); i < params.wots_len(); i++) {
// If the current leaf is part of the signature wots_k stores the chain index
// of the value neccessary for the signature. Otherwise: nullopt (no signature)
const auto wots_k = [&]() -> std::optional<WotsHashIndex> {
if(sign_leaf_idx.has_value() && leaf_idx == sign_leaf_idx.value()) {
return wots_steps[i.get()];
} else {
return std::nullopt;
}
}();
[...]
}
[...]
}
The other two differences in execution relate to the addition of an intermediate value of a hash chain to the WOTS signature data [BOTAN_SLH_DSA_WOTS_SIGN_AND_PKGEN_SIG_NODE_HC]_.
When creating a WOTS signature, the hash chains are only partially run through.
The result is added to the WOTS signature.
This shows the number of steps performed in a hash chain.
This is not critical because this information is also calculated during verification using the message and signature.

.. code-block:: cpp
void wots_sign_and_pkgen(StrongSpan<WotsSignature> sig_out,
StrongSpan<SphincsTreeNode> leaf_out,
const SphincsSecretSeed& secret_seed,
TreeNodeIndex leaf_idx,
std::optional<TreeNodeIndex> sign_leaf_idx,
const std::vector<WotsHashIndex>& wots_steps,
Sphincs_Address& leaf_addr,
Sphincs_Address& pk_addr,
const Sphincs_Parameters& params,
Sphincs_Hash_Functions& hashes) {
[...]
// Iterates down the WOTS chain
for(WotsHashIndex k(0);; k++) {
// Check if this is the value that needs to be saved as a part of the WOTS signature
if(wots_k.has_value() && k == wots_k.value()) {
std::copy(buffer_s.begin(), buffer_s.end(), sig.next<WotsNode>(params.n()).begin());
}
[...]
}
[...]
}

0 comments on commit a1ce11e

Please sign in to comment.