Skip to content

Commit e2eb59a

Browse files
authored
Merge pull request #4 from joii2020/develop_1
1. Refactor code based on feedback 2. Add tools to test data on the chain
2 parents 9a614e5 + 9fc86d3 commit e2eb59a

File tree

20 files changed

+3623
-52
lines changed

20 files changed

+3623
-52
lines changed

Makefile

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
TARGET := riscv64-unknown-linux-gnu-
22
CC := $(TARGET)gcc
33
LD := $(TARGET)gcc
4+
OBJCOPY := $(TARGET)objcopy
45

5-
PARAMS = sphincs-shake-256f
6-
THASH = robust
6+
PARAMS = sphincs-shake-128f
7+
THASH = simple
78

8-
CFLAGS := -fPIC -O3 -fno-builtin-printf -fno-builtin-memcmp -nostdinc -nostartfiles -fvisibility=hidden -fdata-sections -ffunction-sections -nostdlib -Wno-nonnull-compare -DCKB_VM -DCKB_DECLARATION_ONLY
9+
CFLAGS := -fPIC -O3 -fno-builtin-printf -fno-builtin-memcmp -nostdinc -nostartfiles -fvisibility=hidden -fdata-sections -ffunction-sections -nostdlib -Wno-nonnull-compare -DCKB_VM -DCKB_DECLARATION_ONLY -g
910
LDFLAGS := -fdata-sections -ffunction-sections
1011

1112
# Using a new version of gcc will have a warning of ckb-c-stdlib
@@ -66,22 +67,24 @@ ifneq (,$(findstring sha2,$(PARAMS)))
6667
HEADERS += $(SPHINCS_PLUS_DIR)sha2.h
6768
endif
6869

69-
CFLAGS := $(CFLAGS) -g -DCKB_C_STDLIB_PRINTF
70+
# CFLAGS := $(CFLAGS) -DCKB_C_STDLIB_PRINTF
7071

7172
# docker pull nervos/ckb-riscv-gnu-toolchain:gnu-jammy-20230214
7273
BUILDER_DOCKER := nervos/ckb-riscv-gnu-toolchain@sha256:7601a814be2595ad471288fefc176356b31101837a514ddb0fc93b11c1cf5135
7374

7475
all: build/sphincsplus_lock
7576

7677
all-via-docker:
77-
docker run --rm -v `pwd`:/code ${BUILDER_DOCKER} bash -c "cd /code && make"
78+
docker run --rm -v `pwd`:/code ${BUILDER_DOCKER} bash -c "cd /code && make PARAMS=$(PARAMS) THASH=$(THASH)"
7879

7980
build/convert_asm: c/ref/fips202_asm.S
8081
riscv-naive-assembler -i c/ref/fips202_asm.S > c/ref/fips202_asm_bin.S
8182

8283
build/sphincsplus_lock: c/ckb-sphincsplus-lock.c $(SOURCES) $(HEADERS)
8384
mkdir -p build
8485
$(CC) $(CFLAGS) -o $@ $(SOURCES) $<
86+
cp $@ $@.debug
87+
$(OBJCOPY) --strip-debug --strip-all $@
8588

8689
clean:
8790
rm -rf build/sphincsplus_lock

README.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,67 @@
11
# quantum-resistant-lock-script
22
Qantum resistant lock script on CKB, using SPHINCS+
3+
4+
## SPHINCS+
5+
6+
## Build
7+
8+
### Compile contract
9+
``` shell
10+
make all-via-docker
11+
```
12+
13+
### Compile other hash type
14+
``` shell
15+
make all-via-docker PARAMS=sphincs-shake-256f THASH=robust
16+
```
17+
Different hash types will have large performance differences when verifying. For specific performance differences, please refer to the table below. You can also refer to this script to generate and execute contracts (tests/sphincsplus_rust/run_example.sh).
18+
19+
20+
## Performance
21+
Use items for tests/sphincsplus/optimization/run-all-optimization.sh.
22+
The script uses fixed signature data (tests/sphincsplus/test_data/), Because different signature data will have subtle differences.
23+
24+
| | 128s bit | 128f bit | 192s bit | 192f bit | 256s bit | 256f bit |
25+
| ------------- | ---------- | ---------- | ---------- | ---------- | ---------- | ---------- |
26+
| pubkey size | 32 | 32 | 48 | 48 | 64 | 64 |
27+
|signature size | 7888 | 17120 | 16256 | 35696 | 29824 | 49888 |
28+
| shake simple | 16.9M | 49.6M | 25.4M | 73.8M | 37.1M | 72.4M |
29+
| shake robust | 34.3M | 98.4M | 49.1M | 147.5M | 73.2M | 150.3M |
30+
| sha2 simple | 10.7M | 33.9M | 16.8M | 48.7M | 24.7M | 47.5M |
31+
| sha2 robust | 22.5M | 64.5M | 34.1M | 98.6M | 60.4M | 130.3M |
32+
| haraka simple | 27.5M | 73.9M | 39.2M | 105.8M | 60.4M | 114.9M |
33+
| haraka robust | 45.7M | 119.8M | 70.5M | 182.7M | 102.8M | 193.3M |
34+
35+
* Note: Default hash type: **shake-128f-simple** (Verify cycles: about 70M)
36+
37+
## Sample in Dev Blockchain
38+
**Convert a default Lock to ckb-sphincsplus lock script (in ckb dev)**
39+
40+
1. compile. Hera we use the default options.
41+
</br>
42+
Here we will get a sphincsplus_lock file, the size is about 85608bytes.
43+
2. Deploy the compiled contract to the test network.
44+
</br>
45+
We use [ckb-cli](https://github.com/nervosnetwork/ckb-cli) to deploy this contract, You can refer to [here](https://github.com/nervosnetwork/ckb-cli/wiki/Handle-Complex-Transaction#a-demo).
46+
* After the execution is successful, it is recommended to record the tx-hash to facilitate subsequent operations.
47+
3. Generate key file.
48+
</br>
49+
Use this tool: tools/ckb-sphincs-tools.
50+
``` shell
51+
cargo run -- gen-key key.json
52+
```
53+
We can get a set of key files, including public and private keys.
54+
* If the contract you compile does not use the default value, it needs to be the same here.
55+
* Need to save this file.
56+
4. Convert a secp256k1 default lock script to SPHINCS+ lock script.
57+
``` shell
58+
cargo run -- cc_to_sphincsplus --tx_hash <tx-hash> --tx_index <index> --key_file key.json --prikey <You can use ckb-cli account export>
59+
```
60+
5. Convert a SPHINCS+ lock script to secp256k1 default lock script.
61+
``` shell
62+
cargo run -- cc_to_sphincsplus --tx_hash <tx-hash> --tx_index <index> --key_file key.json --lock_arg <LOCK-ARG> --sp_tx_hash <SPHINCS+ Script in step 2> --sp_tx_index <index>
63+
```
64+
65+
66+
## Deployment on testnet
67+
TODO

c/ckb-sphincsplus-lock.c

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,14 @@ enum SPHINCSPLUS_EXAMPLE_ERROR {
5454
ERROR_SPHINCSPLUS_SYSCALL,
5555
ERROR_SPHINCSPLUS_ENCODING,
5656
ERROR_SPHINCSPLUS_ARGS,
57+
ERROR_SPHINCSPLUS_PUBKEY,
5758
ERROR_SPHINCSPLUS_WITNESS,
5859
ERROR_SPHINCSPLUS_VERIFY,
5960
};
6061

6162
#ifdef CKB_VM
6263
// randombytes in sphincs+ depends on fcntl.h and unistd.h
63-
void randombytes(unsigned char *x, unsigned long long xlen) {
64-
ASSERT(false);
65-
}
64+
void randombytes(unsigned char *x, unsigned long long xlen) { ASSERT(false); }
6665
#endif // CKB_VM
6766

6867
static int extract_witness_lock(uint8_t *witness, uint64_t len,
@@ -226,28 +225,43 @@ int make_witness(WitnessArgsType *witness) {
226225
return 0;
227226
}
228227

229-
int get_sign(uint8_t *sign) {
228+
// Witness data structure
229+
// |-----Signature data-----|-----Public Key-----|
230+
int get_sign_info(uint8_t *sign, uint8_t *pubkey) {
230231
int err = CKB_SUCCESS;
231232
size_t sign_size = sphincs_plus_get_sign_size();
233+
size_t pubkey_size = sphincs_plus_get_pk_size();
234+
232235
WitnessArgsType witness_args;
233236

234237
uint8_t witness_data_source[MAX_WITNESS_SIZE] = {0};
238+
BytesOptType mol_lock;
239+
mol2_cursor_t mol_lock_bytes;
240+
size_t out_len;
241+
uint8_t buffer[sign_size + pubkey_size];
242+
235243
g_witness_data_source = witness_data_source;
236244
CHECK(make_witness(&witness_args));
237245

238-
BytesOptType mol_lock = witness_args.t->lock(&witness_args);
246+
mol_lock = witness_args.t->lock(&witness_args);
239247
CHECK2(!mol_lock.t->is_none(&mol_lock), ERROR_SPHINCSPLUS_WITNESS);
240248

241-
mol2_cursor_t mol_lock_bytes = mol_lock.t->unwrap(&mol_lock);
242-
size_t out_len = mol2_read_at(&mol_lock_bytes, sign, sign_size);
249+
mol_lock_bytes = mol_lock.t->unwrap(&mol_lock);
250+
CHECK2(mol_lock_bytes.size == sign_size + pubkey_size,
251+
ERROR_SPHINCSPLUS_WITNESS);
252+
253+
out_len = mol2_read_at(&mol_lock_bytes, buffer, sign_size + pubkey_size);
254+
CHECK2(out_len == sign_size + pubkey_size, ERROR_SPHINCSPLUS_WITNESS);
255+
256+
memcpy(sign, buffer, sign_size);
257+
memcpy(pubkey, buffer + sign_size, pubkey_size);
243258

244-
CHECK2(out_len == sign_size, ERROR_SPHINCSPLUS_WITNESS);
245259
exit:
246260
g_witness_data_source = NULL;
247261
return err;
248262
}
249263

250-
int get_public_key(uint8_t *pub_key) {
264+
int get_public_key_hash(uint8_t *pub_key) {
251265
int err = CKB_SUCCESS;
252266

253267
uint8_t script[SCRIPT_SIZE];
@@ -260,30 +274,39 @@ int get_public_key(uint8_t *pub_key) {
260274

261275
mol_seg_t args_seg = MolReader_Script_get_args(&script_seg);
262276
mol_seg_t args_bytes_seg = MolReader_Bytes_raw_bytes(&args_seg);
263-
size_t pubkey_size = sphincs_plus_get_pk_size();
264-
CHECK2((args_bytes_seg.size == pubkey_size), ERROR_SPHINCSPLUS_ARGS);
265-
memcpy(pub_key, args_bytes_seg.ptr, pubkey_size);
277+
CHECK2((args_bytes_seg.size == BLAKE2B_BLOCK_SIZE), ERROR_SPHINCSPLUS_ARGS);
278+
memcpy(pub_key, args_bytes_seg.ptr, BLAKE2B_BLOCK_SIZE);
266279

267280
exit:
268281
return err;
269282
}
270283

271-
int main() {
272-
int err = CKB_SUCCESS;
284+
int check_pubkey(uint8_t *pubkey, uint8_t *pubkey_hash) {
285+
blake2b_state blake2b_ctx;
286+
blake2b_init(&blake2b_ctx, BLAKE2B_BLOCK_SIZE);
287+
blake2b_update(&blake2b_ctx, pubkey, sphincs_plus_get_pk_size());
288+
uint8_t msg[BLAKE2B_BLOCK_SIZE];
289+
blake2b_final(&blake2b_ctx, msg, sizeof(msg));
273290

274-
// signature data size depends on args data(hash type)
275-
uint8_t pubkey[sphincs_plus_get_pk_size()];
276-
err = get_public_key(pubkey);
277-
if (err) {
278-
return err;
291+
if (memcmp(pubkey_hash, msg, BLAKE2B_BLOCK_SIZE)) {
292+
return ERROR_SPHINCSPLUS_PUBKEY;
293+
} else {
294+
return 0;
279295
}
296+
}
280297

298+
int main() {
299+
int err = CKB_SUCCESS;
300+
301+
uint8_t pubkey_hash[BLAKE2B_BLOCK_SIZE];
281302
uint8_t message[BLAKE2B_BLOCK_SIZE];
282303
uint8_t sign[sphincs_plus_get_sign_size()];
283-
CHECK(generate_sighash_all(message, BLAKE2B_BLOCK_SIZE));
284-
285-
CHECK(get_sign(sign));
304+
uint8_t pubkey[sphincs_plus_get_pk_size()];
286305

306+
CHECK(get_public_key_hash(pubkey_hash));
307+
CHECK(generate_sighash_all(message, BLAKE2B_BLOCK_SIZE));
308+
CHECK(get_sign_info(sign, pubkey));
309+
CHECK(check_pubkey(pubkey, pubkey_hash));
287310
err = sphincs_plus_verify(sign, sphincs_plus_get_sign_size(), message,
288311
BLAKE2B_BLOCK_SIZE, pubkey,
289312
sphincs_plus_get_pk_size());

tests/sphincsplus/optimization/Makefile

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ TARGET := riscv64-unknown-linux-gnu-
22
CC := $(TARGET)gcc
33
LD := $(TARGET)gcc
44

5-
PARAMS = sphincs-shake-256f
6-
THASH = robust
5+
PARAMS = sphincs-shake-128f
6+
THASH = simple
77

8-
CFLAGS := -fPIC -O3 -fno-builtin-printf -fno-builtin-memcmp -nostdinc -nostartfiles -fvisibility=hidden -fdata-sections -ffunction-sections -nostdlib -Wno-nonnull-compare -DCKB_VM -DCKB_DECLARATION_ONLY
8+
CFLAGS := -fPIC -O3 -fno-builtin-printf -fno-builtin-memcmp -nostdinc -nostartfiles -fvisibility=hidden -fdata-sections -ffunction-sections -nostdlib -Wno-nonnull-compare -DCKB_VM -DCKB_DECLARATION_ONLY -g -DCKB_C_STDLIB_PRINTF
99
LDFLAGS := -fdata-sections -ffunction-sections
1010

1111
# Using a new version of gcc will have a warning of ckb-c-stdlib
@@ -72,12 +72,13 @@ BUILDER_DOCKER := nervos/ckb-riscv-gnu-toolchain@sha256:7601a814be2595ad471288fe
7272
all: build/verify
7373

7474
all-via-docker:
75-
docker run --rm -v `pwd`:/code ${BUILDER_DOCKER} bash -c "cd /code && make"
75+
cd ../../../ && docker run --rm -v `pwd`:/code ${BUILDER_DOCKER} bash -c "cd /code/tests/sphincsplus/optimization && make PARAMS=$(PARAMS) THASH=$(THASH)"
7676

7777
build/verify: optimization-sphincsplus.c $(SOURCES) $(HEADERS)
7878
$(CC) $(CFLAGS) -o $@ $(SOURCES) $<
7979

8080
run: build/verify
81+
export RUST_LOG=debug
8182
ckb-debugger --bin $< --max-cycles=10000000000
8283

8384
FLAME_GRAPH_DIR := ~/code/tmp/FlameGraph/

tests/sphincsplus/optimization/optimization-sphincsplus.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ int main() {
2323
return 2;
2424
}
2525

26-
printf("Done");
26+
printf("PubKey size: %d, Sign size: %d\n", sizeof(G_TEST_DATA_PUB_KEY),
27+
sizeof(G_TEST_DATA_SIGN));
28+
// printf("Done");
2729
return 0;
2830
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
workdir=$(
2+
cd $(dirname $0)
3+
pwd
4+
)
5+
6+
cd $workdir
7+
8+
HASH_NAMES="shake sha2 haraka"
9+
HASH_SIZES="128 192 256"
10+
HASH_OPTIONS="s f"
11+
THASHS="simple robust"
12+
for HASH_NAME in ${HASH_NAMES[@]}; do
13+
for HASH_SIZE in ${HASH_SIZES[@]}; do
14+
for HASH_OPTION in ${HASH_OPTIONS[@]}; do
15+
for THASH in ${THASHS[@]}; do
16+
PARAMS="sphincs-$HASH_NAME-$HASH_SIZE$HASH_OPTION"
17+
echo "-----------------------------------------------"
18+
echo $PARAMS $THASH
19+
20+
make clean > /dev/null
21+
make all-via-docker PARAMS=$PARAMS THASH=$THASH > /dev/null
22+
ckb-debugger --bin build/verify --max-cycles=10000000000
23+
24+
if (($? == 0)); then
25+
echo "success"
26+
else
27+
exit 1
28+
fi
29+
30+
done
31+
done
32+
done
33+
done

tests/sphincsplus_rust/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ version = "0.1.0"
66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
77

88
[features]
9-
default = ["shake", "hash_256", "hash_options_f", "thashes_robust"]
9+
default = ["shake", "hash_128", "hash_options_f", "thashes_simple"]
1010
haraka = []
1111
sha2 = []
1212
shake = []

tests/sphincsplus_rust/examples/run_base.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub const MAX_CYCLES: u64 = std::u64::MAX;
1111

1212
fn main() {
1313
let mut config = TestConfig::new();
14+
config.print_time = true;
1415

1516
let mut dummy = DummyDataLoader::new();
1617

@@ -23,5 +24,5 @@ fn main() {
2324
verifier.set_debug_printer(debug_printer);
2425
let verify_result = verifier.verify(MAX_CYCLES);
2526
let res = verify_result.expect("pass verification");
26-
println!("cycles: {}", res);
27+
println!("cycles: {} ({:.2?}M)", res, (res as f64) / 1024.0 / 1024.0);
2728
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
if [ ! -n "$1" ] ;then
2+
HASH_NAME="shake"
3+
HASH_SIZE="128"
4+
THASH="simple"
5+
HASH_OPTION="f"
6+
else
7+
HASH_NAME=$1
8+
HASH_SIZE=$2
9+
THASH=$3
10+
HASH_OPTION=$4
11+
fi
12+
PARAMS="sphincs-$HASH_NAME-$HASH_SIZE$HASH_OPTION"
13+
14+
#!/bin/bash
15+
workdir=$(
16+
cd $(dirname $0)/../../
17+
pwd
18+
)
19+
20+
cd $workdir
21+
22+
rm -rf build/*
23+
mkdir -p build
24+
25+
make all-via-docker PARAMS=$PARAMS THASH=$THASH > /dev/null
26+
if (($? != 0)); then
27+
exit 1
28+
fi
29+
30+
cd tests/sphincsplus_rust
31+
cargo clean > /dev/null
32+
cargo build --examples --no-default-features --features "$HASH_NAME hash_$HASH_SIZE hash_options_$HASH_OPTION thashes_$THASH" > /dev/null 2>&1
33+
./target/debug/examples/run_base
34+
if (($? != 0)); then
35+
exit 1
36+
fi

tests/sphincsplus_rust/run_rust.sh

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
if [ ! -n "$1" ] ;then
2-
PARAMS="sphincs-shake-256f"
2+
HASH_NAME="shake"
3+
HASH_SIZE="256"
34
THASH="robust"
5+
HASH_OPTION="f"
46
else
57
HASH_NAME=$1
68
HASH_SIZE=$2
79
THASH=$3
810
HASH_OPTION=$4
9-
PARAMS="sphincs-$HASH_NAME-$HASH_SIZE$HASH_OPTION"
1011
fi
12+
PARAMS="sphincs-$HASH_NAME-$HASH_SIZE$HASH_OPTION"
1113

1214
#!/bin/bash
1315
workdir=$(
@@ -29,7 +31,7 @@ fi
2931

3032
cd tests/sphincsplus_rust
3133
cargo clean
32-
cargo test
34+
cargo test --no-default-features --features "$HASH_NAME hash_$HASH_SIZE hash_options_$HASH_OPTION thashes_$THASH"
3335
if (($? == 0)); then
3436
echo "success"
3537
else

0 commit comments

Comments
 (0)