Skip to content

Instantly share code, notes, and snippets.

@zambonin
Created April 5, 2026 04:44
Show Gist options
  • Select an option

  • Save zambonin/b1c93cd77c36dd7229cf67b31cf0607d to your computer and use it in GitHub Desktop.

Select an option

Save zambonin/b1c93cd77c36dd7229cf67b31cf0607d to your computer and use it in GitHub Desktop.
Code to reproduce the single table of 10.3390/info14100523.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from math import ceil
from textwrap import fill
from typing import Dict
from warnings import catch_warnings, filterwarnings
with catch_warnings():
filterwarnings(
"ignore",
category=UserWarning,
message="liboqs version.*differs from liboqs-python version.*",
)
from oqs import Signature
CERT_HEADER_BYTES = {
r"\kappa_{\mathsf{min}}": 128,
r"\kappa_{\mathsf{avg}}": 512,
r"\kappa_{\mathsf{max}}": 1024 + 512,
}
SIGNATURES_PER_YEAR = (1 << 24, 1 << 27)
# https://arxiv.org/pdf/2105.10520
ETH_BASE_TX_GAS = 21000
ETH_MARGINAL_WORD_GAS = 22100
EVM_WORD_SIZE_BYTES = 32
def get_signature_sizes() -> Dict[int, Dict[str, int]]:
oqs_names = {
256: {
"Falcon-512": "Falcon-512",
"Dilithium-2": "Dilithium2",
"SPHINCS$^{+}$-128s": "SPHINCS+-SHA2-128s-simple",
},
384: {
"Dilithium-3": "Dilithium3",
"SPHINCS$^{+}$-192s": "SPHINCS+-SHA2-192s-simple",
},
512: {
"Falcon-1024": "Falcon-1024",
"Dilithium-5": "Dilithium5",
"SPHINCS$^{+}$-256s": "SPHINCS+-SHA2-256s-simple",
},
}
sizes = {256: {}, 384: {}, 512: {}}
for sec_level, algs in oqs_names.items():
for display_name, alg_name in algs.items():
with Signature(alg_name) as sig:
sizes[sec_level][display_name] = sig.details["length_signature"]
return sizes
def compute_tuple_bytes(n: int, kappa: int, psi: int) -> int:
return kappa + n // 8 + psi + n // 8
def print_latex_header(caption: str) -> None:
print("\\begin{table}[htbp]\n \centering")
wrapped_caption = fill(
f"\\caption{{{caption}}}",
width=80,
initial_indent=" ",
subsequent_indent=" ",
)
print(wrapped_caption)
print(
" \\begin{tabular}{llrrrrrr}\n"
" \\toprule\n"
" \multirow{2}{*}{$n$} & \multirow{2}{*}{Algorithm} "
"& \multicolumn{3}{c}{$\sigma_{\\text{low}}$} "
"& \multicolumn{3}{c}{$\sigma_{\\text{high}}$} \\\\\n"
" \cmidrule(lr){3-5} \cmidrule(lr){6-8}\n"
" & & $\kappa_{\\text{min}}$ & $\kappa_{\\text{avg}}$ "
"& $\kappa_{\\text{max}}$ & $\kappa_{\\text{min}}$ "
"& $\kappa_{\\text{avg}}$ & $\kappa_{\\text{max}}$ \\\\\n"
" \midrule"
)
def print_latex_footer() -> None:
print(" \\bottomrule\n \end{tabular}\n\end{table}")
def gas_table(n: int, sizes: Dict[str, int]) -> None:
line = (
" {:<4s} & {:<20s} "
"& ${:>9d}$ & ${:>9d}$ & ${:>9d}$ "
"& ${:>9d}$ & ${:>9d}$ & ${:>9d}$ \\\\"
)
first_alg = True
for name_psi, psi in sizes.items():
n_str = str(n) if first_alg else ""
gas = [n_str, name_psi]
for _ in SIGNATURES_PER_YEAR:
for _, kappa in CERT_HEADER_BYTES.items():
one_tuple_bytes = compute_tuple_bytes(n, kappa, psi)
words = ceil(one_tuple_bytes / EVM_WORD_SIZE_BYTES)
cost = int(ETH_BASE_TX_GAS + (ETH_MARGINAL_WORD_GAS * words))
gas.append(cost)
print(line.format(*gas))
first_alg = False
def storage_table(n: int, sizes: Dict[str, int]) -> None:
line = (
" {:<4s} & {:<20s} "
"& ${:>7.2f}$ & ${:>7.2f}$ & ${:>7.2f}$ "
"& ${:>7.2f}$ & ${:>7.2f}$ & ${:>7.2f}$ \\\\"
)
first_alg = True
for name_psi, psi in sizes.items():
n_str = str(n) if first_alg else ""
data = [n_str, name_psi]
for scenario in SIGNATURES_PER_YEAR:
for _, kappa in CERT_HEADER_BYTES.items():
one_tuple_bytes = compute_tuple_bytes(n, kappa, psi)
data.append(scenario * one_tuple_bytes / (1 << 30))
print(line.format(*data))
first_alg = False
if __name__ == "__main__":
signature_sizes = get_signature_sizes()
storage_caption = (
"Uncompressed storage size estimation of a blockchain containing "
"bindings between certificates and hashes, in GB/year, considering "
"various security levels $n$, several quantum-resistant signature "
"algorithms and parameters, certificate sizes $\kappa$ and two "
"contrasting signature usage scenarios $\sigma_{\\text{low}}$ and "
"$\sigma_{\\text{high}}$."
)
print_latex_header(storage_caption)
for i, security_parameter in enumerate((256, 384, 512)):
if i > 0:
print(r" \midrule")
storage_table(security_parameter, signature_sizes[security_parameter])
print_latex_footer()
gas_caption = (
"Ethereum EVM gas cost estimation for verifying individual bindings "
"between certificates and hashes, considering various security levels "
"$n$, several quantum-resistant signature algorithms and parameters, "
"certificate sizes $\kappa$ and two contrasting signature usage "
"scenarios $\sigma_{\\text{low}}$ and $\sigma_{\\text{high}}$."
)
print_latex_header(gas_caption)
for i, security_parameter in enumerate((256, 384, 512)):
if i > 0:
print(r" \midrule")
gas_table(security_parameter, signature_sizes[security_parameter])
print_latex_footer()
TARGET = table.txt
PDF_TARGET = $(TARGET:.txt=.pdf)
.INTERMEDIATE: $(TARGET:.txt=.tex)
$(TARGET): build-table.py /usr/bin/docker
docker run --rm -v ".:/app" -w /app openquantumsafe/python \
python3 $< > $@
%.tex: %.txt
( echo "\documentclass{article}" \
"\usepackage{booktabs,multirow,amsmath}" \
"\usepackage[margin=.5in]{geometry}" \
"\begin{document}" ) > $@
cat $< >> $@
echo "\end{document}" >> $@
%.pdf: %.tex
latexmk -interaction=nonstopmode -shell-escape -pdf -use-make -cd $<
standalone: $(PDF_TARGET)
clean:
latexmk -CA -f $(PDF_TARGET)
$(RM) $(TARGET)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment