This repository provides the supplementary materials for the WhibOx Contest Edition 4. Specifically, it has the following three main contents:
- A CMD tool for generate a key pair given a seed.
- CMD tools for generating / verifying EC-Schnorr signature.
- The reference implementation in C using the GNU GMP library.
In order to use this repository, one has to install
- GNU Make (tested with version 4.3)
- GCC version 13.2.1
- Python 3.11 (tested with version 3.11.9)
- Click 7.x (test with version 8.1.7)
- GNU GMP version 6.3.0
- Optional: libseccomp2 version 2.5.5
SECCOMP BPF filters are used for system calls filtering, and reflects the compilation on the WhibOx server side. Since SECCOMP has adherence to the Linux kernel, you can compile the project without it with:
$ NO_SECCOMP=1 make
This will deactivate the SECCOMP usage for testing purposes on non-Linux platforms (e.g. Mac OS) or if one wants to perform tests without the system calls filtering.
$ ./keygen.py CHES2024 seed: seed = CHES2024 private key: d = DE6B0B3C9F75548B0EBC6FA5D9FFDA83AD5306F88A403EDA624BDC27C15B00BC public key: Q = (x = B3AF1DC41998870D6B574BCF5C0935BB1F21C33A736FED99D8C50FC7CB038AB4, y = B3BA1B8C2F68037D149F2E0500C0B6C7D5BAEEB47B4533809FD3D902ECE63345) encoded public key: B3AF1DC41998870D6B574BCF5C0935BB1F21C33A736FED99D8C50FC7CB038AB4B3BA1B8C2F68037D149F2E0500C0B6C7D5BAEEB47B4533809FD3D902ECE63345
Notice the signature here is not deterministic.
./ec_schnorr_sign.py DE6B0B3C9F75548B0EBC6FA5D9FFDA83AD5306F88A403EDA624BDC27C15B00BC Signature: BC50F1A20D08A0530BE7B56E668E85473CF7769C982B8EF6E7F9791D016F7CEB5F0EEFA5927576F470C965BF37461BAC67D5C5F774ECB08B78925B178FDC63E4 $ ./ec_schnorr_verify.py B3AF1DC41998870D6B574BCF5C0935BB1F21C33A736FED99D8C50FC7CB038AB4B3BA1B8C2F68037D149F2E0500C0B6C7D5BAEEB47B4533809FD3D902ECE63345 BC50F1A20D08A0530BE7B56E668E85473CF7769C982B8EF6E7F9791D016F7CEB5F0EEFA5927576F470C965BF37461BAC67D5C5F774ECB08B78925B178FDC63E4 Good signature :)
The source code of the reference implementation can be found in dECDSA.c.
Try make && ./dECDSA
to test it with the SECCOMP filtering (reflecting what the WhibOx server will execute), or
with NO_SECCOMP=1 make && ./dECDSA
without the SECCOMP filtering for testing purposes.
$ xxd -ps -c 64 -u test_hash F7FD41E28DFCCA32C1CEEF637C202CA6E99E57F18AFEF957DF0866B4CDD60F5C $ ./dECDSA <test_hash | xxd -ps -c 128 -u 8007ABC1CD96650531BD8039893E8CF549A52D26E2A8A0E4700087523A7156A4AA0A7464CCA7BB14EB75FDC829034CFE82E5C47EE30E07B17B75F387ECBB7168 ./ecdsa_verify.py B3AF1DC41998870D6B574BCF5C0935BB1F21C33A736FED99D8C50FC7CB038AB4B3BA1B8C2F68037D149F2E0500C0B6C7D5BAEEB47B4533809FD3D902ECE63345 F7FD41E28DFCCA32C1CEEF637C202CA6E99E57F18AFEF957DF0866B4CDD60F5C 8007ABC1CD96650531BD8039893E8CF549A52D26E2A8A0E4700087523A7156A4AA0A7464CCA7BB14EB75FDC829034CFE82E5C47EE30E07B17B75F387ECBB7168 Good signature :)
The system calls that are allowed in main.c are: rt_sigreturn
, exit
, exit_group
, read
on file descriptor 0 (i.e. stdin
), write
on
file descriptor 1 (i.e. stdout
), brk
, mmap
(with the restriction of non-executable pages, i.e. PROT_EXEC
is forbidden), and munmap
. Any system call
not conforming to this list will induce a SIGSYS
or SIGSEGV
immediately killing the process.
For debugging purposes to check if your binary is compliant and if not why, you can use the strace
utility
(it should be packaged on most Linux distros) that logs all the system calls. For instance,
on the reference implementation compiled with the default options (i.e. with active SECCOMP):
$ strace ./dECDSA < test_hash | xxd -ps -c 128 -u execve("./dECDSA", ["./dECDSA"], 0x7fff8395d930 /* 58 vars */) = 0 brk(NULL) = 0x55b01e8f1000 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fb9917ec000 ... brk(NULL) = 0x55b01e8f1000 brk(0x55b01e912000) = 0x55b01e912000 prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) = 0 seccomp(SECCOMP_SET_MODE_FILTER, 0, 0x55b01e8f3040) = 0 read(0, 0x7fff0f457270, 32) = 32 write(1, 0x7fff0f457290, 64) = 64 read(0, "", 32) = 0 exit_group(0) = ? +++ exited with 0 +++ 8007ABC1CD96650531BD8039893E8CF549A52D26E2A8A0E4700087523A7156A4AA0A7464CCA7BB14EB75FDC829034CFE82E5C47EE30E07B17B75F387ECBB7168
As one can see, everything went fine since the program exited with 0
and the expected output is indeed printed on the console.
On the same implementation where we add a printf("[+] Hello from ECDSA_256_sign\n");
at the very beginning of the
ECDSA_256_sign
function, we get:
$ strace ./dECDSA < test_hash | xxd -ps -c 128 -u execve("./dECDSA", ["./dECDSA"], 0x7fff781d34f0 /* 58 vars */) = 0 brk(NULL) = 0x562914847000 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f607ba64000 ... read(0, 0x7ffe21400340, 32) = 32 newfstatat(1, 0x7f889a0d5bd5, 0x7ffe214000c0, AT_EMPTY_PATH) = 262 +++ killed by SIGSYS +++
Note the killed by SIGSYS
confirming that the process was indeed killed because of SECCOMP. This is due in this case
to the usage of the newfstatat
system call by the libc
in the underlying printf
implementation.
If you want to locally reproduce the excat compilation process with SECCOMP that will take place on the server, you can look at the submission server repository and how to locally deploy it.