Skip to content

Commit

Permalink
add support for secp256r1
Browse files Browse the repository at this point in the history
add tests for secp256r1

fix secp256r1 test

create separated auth programs

remove lazy static

remove verbose outputs, add more comments

add docs for secp256r1
  • Loading branch information
contrun committed Aug 14, 2023
1 parent cb58c5c commit c5fc603
Show file tree
Hide file tree
Showing 12 changed files with 590 additions and 39 deletions.
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@
[submodule "deps/ed25519"]
path = deps/ed25519
url = https://github.com/nervosnetwork/ed25519.git
[submodule "deps/libecc"]
path = deps/libecc
url = https://github.com/cryptape/libecc.git
branch = riscv-optimized
30 changes: 24 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,20 @@ PASSED_MBEDTLS_CFLAGS := $(CFLAGS) -DCKB_DECLARATION_ONLY -I ../../ckb-c-stdlib-

SECP256K1_SRC_20210801 := deps/secp256k1-20210801/src/ecmult_static_pre_context.h

CFLAGS_LIBECC := -fno-builtin -DUSER_NN_BIT_LEN=256 -DWORDSIZE=64 -DWITH_STDLIB -DWITH_BLANK_EXTERNAL_DEPENDENCIES -DCKB_DECLARATION_ONLY -fPIC -g -O3
LIBECC_OPTIMIZED_PATH := deps/libecc
LIBECC_OPTIMIZED_FILES := ${LIBECC_OPTIMIZED_PATH}/build/libarith.a ${LIBECC_OPTIMIZED_PATH}/build/libec.a ${LIBECC_OPTIMIZED_PATH}/build/libsign.a
CFLAGS_LIBECC_OPTIMIZED = $(CFLAGS_LIBECC) -DWITH_LL_U256_MONT
CFLAGS_LINK_TO_LIBECC_OPTIMIZED := -fno-builtin -fno-builtin-printf -DWORDSIZE=64 -DWITH_STDLIB -DWITH_BLANK_EXTERNAL_DEPENDENCIES -I ${LIBECC_OPTIMIZED_PATH}/src -I ${LIBECC_OPTIMIZED_PATH}/src/external_deps

# docker pull nervos/ckb-riscv-gnu-toolchain:gnu-jammy-20230214
BUILDER_DOCKER := nervos/ckb-riscv-gnu-toolchain@sha256:d3f649ef8079395eb25a21ceaeb15674f47eaa2d8cc23adc8bcdae3d5abce6ec

all: build/secp256k1_data_info_20210801.h $(SECP256K1_SRC_20210801) deps/mbedtls/library/libmbedcrypto.a build/auth build/always_success
all: build/secp256k1_data_info_20210801.h $(SECP256K1_SRC_20210801) deps/mbedtls/library/libmbedcrypto.a build/auth-libecc build/auth build/always_success

all-via-docker: ${PROTOCOL_HEADER}
mkdir -p build
docker run --rm -v `pwd`:/code ${BUILDER_DOCKER} bash -c "cd /code && make"
docker run --rm -v `pwd`:/code ${BUILDER_DOCKER} bash -c "cd /code && make all"

build/always_success: c/always_success.c
$(CC) $(AUTH_CFLAGS) $(LDFLAGS) -o $@ $<
Expand All @@ -48,6 +54,11 @@ $(SECP256K1_SRC_20210801):
CC=$(CC) LD=$(LD) ./configure --with-bignum=no --enable-ecmult-static-precomputation --enable-endomorphism --enable-module-recovery --host=$(TARGET) && \
make src/ecmult_static_pre_context.h src/ecmult_static_context.h

$(LIBECC_OPTIMIZED_FILES): libecc

libecc:
make -C ${LIBECC_OPTIMIZED_PATH} LIBECC_WITH_LL_U256_MONT=1 CC=${CC} LD=${LD} CFLAGS="$(CFLAGS_LIBECC_OPTIMIZED)"

deps/mbedtls/library/libmbedcrypto.a:
cp deps/mbedtls-config-template.h deps/mbedtls/include/mbedtls/config.h
make -C deps/mbedtls/library APPLE_BUILD=0 AR=$(AR) CC=${CC} LD=${LD} CFLAGS="${PASSED_MBEDTLS_CFLAGS}" LDFLAGS="${LDFLAGS}" libmbedcrypto.a
Expand All @@ -64,22 +75,29 @@ build/libed25519.a: build/ed25519/sign.o build/ed25519/verify.o build/ed25519/sh
build/ed25519/key_exchange.o build/ed25519/ge.o build/ed25519/fe.o build/ed25519/add_scalar.o
$(AR) cr $@ $^

build/auth: c/auth.c c/cardano/cardano_lock_inc.h c/ripple.h deps/mbedtls/library/libmbedcrypto.a build/libed25519.a build/libnanocbor.a
$(CC) $(AUTH_CFLAGS) $(LDFLAGS) -fPIE -pie -Wl,--dynamic-list c/auth.syms -o $@ $^
build/auth: c/auth.c c/cardano/cardano_lock_inc.h c/ripple.h deps/mbedtls/library/libmbedcrypto.a build/libed25519.a build/libnanocbor.a $(LIBECC_OPTIMIZED_FILES)
$(CC) $(AUTH_CFLAGS) $(CFLAGS_LINK_TO_LIBECC_OPTIMIZED) $(LDFLAGS) -fPIE -pie -Wl,--dynamic-list c/auth.syms -o $@ $^
cp $@ $@.debug
$(OBJCOPY) --strip-debug --strip-all $@
ls -l $@

build/auth-libecc: c/auth.c c/cardano/cardano_lock_inc.h c/ripple.h deps/mbedtls/library/libmbedcrypto.a build/libed25519.a build/libnanocbor.a $(LIBECC_OPTIMIZED_FILES)
$(CC) $(AUTH_CFLAGS) -DLIBECC_ONLY $(CFLAGS_LINK_TO_LIBECC_OPTIMIZED) $(LDFLAGS) -fPIE -pie -Wl,--dynamic-list c/auth.syms -o $@ $^
cp $@ $@.debug
$(OBJCOPY) --strip-debug --strip-all $@
ls -l build/auth
ls -l $@

fmt:
clang-format -i -style="{BasedOnStyle: Google, IndentWidth: 4}" c/*.c c/*.h

clean:
rm -rf build/*.debug
rm -f build/auth build/auth_demo
rm -f build/auth build/auth-libecc build/auth_demo
rm -rf build/secp256k1_data_info_20210801.h build/dump_secp256k1_data_20210801
rm -rf build/ed25519 build/libed25519.a build/nanocbor build/libnanocbor.a
cd deps/secp256k1-20210801 && [ -f "Makefile" ] && make clean
make -C deps/mbedtls/library clean
make -C deps/libecc clean

.PHONY: all all-via-docker

59 changes: 43 additions & 16 deletions c/auth.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
// clang-format off
#include "errors.h"

#ifdef LIBECC_ONLY
#include "secp256r1.h"
#endif

#include "mbedtls/md.h"
#include "mbedtls/md_internal.h"
#include "mbedtls/memory_buffer_alloc.h"
Expand Down Expand Up @@ -77,22 +83,6 @@
#define SOLANA_BLOCKHASH_SIZE 32
#define SOLANA_MESSAGE_HEADER_SIZE 3

enum AuthErrorCodeType {
ERROR_NOT_IMPLEMENTED = 100,
ERROR_MISMATCHED,
ERROR_INVALID_ARG,
ERROR_WRONG_STATE,
// spawn
ERROR_SPAWN_INVALID_LENGTH,
ERROR_SPAWN_SIGN_TOO_LONG,
ERROR_SPAWN_INVALID_ALGORITHM_ID,
ERROR_SPAWN_INVALID_SIG,
ERROR_SPAWN_INVALID_MSG,
ERROR_SPAWN_INVALID_PUBKEY,
// schnorr
ERROR_SCHNORR,
};

typedef int (*validate_signature_t)(void *prefilled_data, const uint8_t *sig,
size_t sig_len, const uint8_t *msg,
size_t msg_len, uint8_t *output,
Expand Down Expand Up @@ -625,6 +615,35 @@ int validate_signature_solana(void *prefilled_data, const uint8_t *sig,
return err;
}

#ifdef LIBECC_ONLY
int validate_signature_secp256r1(void *prefilled_data, const uint8_t *sig,
size_t sig_len, const uint8_t *msg,
size_t msg_len, uint8_t *output,
size_t *output_len) {
int err = 0;

if (*output_len < BLAKE160_SIZE) {
return SECP256K1_PUBKEY_SIZE;
}
CHECK2(msg_len == BLAKE2B_BLOCK_SIZE, ERROR_INVALID_ARG);
CHECK2(sig_len == SECP256R1_DATA_SIZE, ERROR_INVALID_ARG);
const uint8_t *signature_ptr = sig;
const uint8_t *pub_key_ptr = signature_ptr + SECP256R1_SIGNATURE_SIZE;

CHECK(secp256r1_verify_signature(signature_ptr, SECP256R1_SIGNATURE_SIZE, pub_key_ptr, SECP256R1_PUBKEY_SIZE, msg, msg_len ));

blake2b_state ctx;
uint8_t pubkey_hash[BLAKE2B_BLOCK_SIZE] = {0};
blake2b_init(&ctx, BLAKE2B_BLOCK_SIZE);
blake2b_update(&ctx, pub_key_ptr, SECP256R1_PUBKEY_SIZE);
blake2b_final(&ctx, pubkey_hash, sizeof(pubkey_hash));

memcpy(output, pubkey_hash, BLAKE160_SIZE);
*output_len = BLAKE160_SIZE;
exit:
return err;
}
#endif

int convert_copy(const uint8_t *msg, size_t msg_len, uint8_t *new_msg,
size_t new_msg_len) {
Expand Down Expand Up @@ -993,6 +1012,13 @@ __attribute__((visibility("default"))) int ckb_auth_validate(
CHECK2(message_size > 0, ERROR_INVALID_ARG);
CHECK2(pubkey_hash_size == BLAKE160_SIZE, ERROR_INVALID_ARG);

#ifdef LIBECC_ONLY
if (auth_algorithm_id == AuthAlgorithmIdSecp256R1) {
err = verify(pubkey_hash, signature, signature_size, message,
message_size, validate_signature_secp256r1, convert_copy);
CHECK(err);
}
#else
if (auth_algorithm_id == AuthAlgorithmIdCkb) {
CHECK2(signature_size == SECP256K1_SIGNATURE_SIZE, ERROR_INVALID_ARG);
err = verify(pubkey_hash, signature, signature_size, message,
Expand Down Expand Up @@ -1058,6 +1084,7 @@ __attribute__((visibility("default"))) int ckb_auth_validate(
} else {
CHECK2(false, ERROR_NOT_IMPLEMENTED);
}
#endif
exit:
return err;
}
Expand Down
1 change: 1 addition & 0 deletions c/ckb_auth.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ enum AuthAlgorithmIdType {
AuthAlgorithmIdMonero = 12,
AuthAlgorithmIdSolana = 13,
AuthAlgorithmIdRipple = 14,
AuthAlgorithmIdSecp256R1 = 15,
AuthAlgorithmIdOwnerLock = 0xFC,
};

Expand Down
18 changes: 18 additions & 0 deletions c/errors.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef _CBK_ERRORS_H_
#define _CBK_ERRORS_H_
enum AuthErrorCodeType {
ERROR_NOT_IMPLEMENTED = 100,
ERROR_MISMATCHED,
ERROR_INVALID_ARG,
ERROR_WRONG_STATE,
// spawn
ERROR_SPAWN_INVALID_LENGTH,
ERROR_SPAWN_SIGN_TOO_LONG,
ERROR_SPAWN_INVALID_ALGORITHM_ID,
ERROR_SPAWN_INVALID_SIG,
ERROR_SPAWN_INVALID_MSG,
ERROR_SPAWN_INVALID_PUBKEY,
// schnorr
ERROR_SCHNORR,
};
#endif // _CBK_ERRORS_H_
114 changes: 114 additions & 0 deletions c/secp256r1.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#include <stdio.h>

#include "errors.h"

#include "lib_ecc_types.h"
#include "libec.h"
#include "libsig.h"

#define SECP256R1_SIGNATURE_SIZE 64
#define SECP256R1_PUBKEY_SIZE 64
#define SECP256R1_DATA_SIZE (SECP256R1_SIGNATURE_SIZE + SECP256R1_PUBKEY_SIZE)

static const char *ec_name = "SECP256R1";
static const char *ec_sig_name = "ECDSA";
static const char *hash_algorithm = "SHA256";

const uint32_t projective_buffer_size = 96;
const uint32_t affine_buffer_size = 64;

int get_random(unsigned char *buf, u16 len) {
for (int i = 0; i < len; i++) {
buf[i] = 0;
}
return 0;
}

static int string_to_params(const char *ec_name, const char *ec_sig_name,
ec_sig_alg_type *sig_type,
const ec_str_params **ec_str_p,
const char *hash_name, hash_alg_type *hash_type) {
const ec_str_params *curve_params;
const ec_sig_mapping *sm;
const hash_mapping *hm;
uint32_t curve_name_len;

if (sig_type != NULL) {
sm = get_sig_by_name(ec_sig_name);
if (!sm) {
return ERROR_INVALID_ARG;
}
*sig_type = sm->type;
}

if (ec_str_p != NULL) {
curve_name_len = local_strlen((const char *)ec_name) + 1;
if (curve_name_len > 255) {
return ERROR_INVALID_ARG;
}
curve_params = ec_get_curve_params_by_name((const uint8_t *)ec_name,
(uint8_t)curve_name_len);
if (!curve_params) {
return ERROR_INVALID_ARG;
}
*ec_str_p = curve_params;
}

if (hash_type != NULL) {
hm = get_hash_by_name(hash_name);
if (!hm) {
return ERROR_INVALID_ARG;
}
*hash_type = hm->type;
}

return 0;
}

void convert_aff_buf_to_prj_buf(const uint8_t *aff_buf, uint32_t aff_buf_len,
uint8_t *prj_buf, uint32_t prj_buf_len) {
static const uint8_t z_buf[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01};
MUST_HAVE(aff_buf_len == affine_buffer_size);
MUST_HAVE(prj_buf_len == projective_buffer_size);
memcpy(prj_buf, aff_buf, aff_buf_len);
memcpy(prj_buf + aff_buf_len, z_buf, sizeof(z_buf));
}

int secp256r1_verify_signature(const uint8_t *sig, uint8_t siglen,
const uint8_t *pk, uint32_t pklen,
const uint8_t *m, uint32_t mlen) {
const ec_str_params *ec_str_p;
ec_sig_alg_type sig_type;
hash_alg_type hash_type;
ec_pub_key pub_key;
ec_params params;
int ret;

uint8_t pj_pk_buf[projective_buffer_size];
convert_aff_buf_to_prj_buf(pk, pklen, pj_pk_buf, sizeof(pj_pk_buf));

MUST_HAVE(ec_name != NULL);

ret = string_to_params(ec_name, ec_sig_name, &sig_type, &ec_str_p,
hash_algorithm, &hash_type);
if (ret) {
return ERROR_INVALID_ARG;
}
import_params(&params, ec_str_p);

ret = ec_pub_key_import_from_buf(&pub_key, &params, pj_pk_buf,
sizeof(pj_pk_buf), sig_type);
if (ret) {
return ERROR_INVALID_ARG;
}

ret = ec_verify(sig, siglen, &pub_key, m, mlen, sig_type, hash_type);
if (ret) {
return ERROR_SPAWN_INVALID_SIG;
}

return 0;
}
1 change: 1 addition & 0 deletions deps/libecc
Submodule libecc added at 96de53
71 changes: 71 additions & 0 deletions docs/secp256r1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# ckb-auth secp256r1 interoperability
Ckb-auth library is able to verify ECDSA signatures
A simple way to use litecoin signature algorithm to lock ckb cells
is to sign the transaction hash (or maybe `sighash_all`, i.e. hashing all fields
including transaction hash and other witnesses in this input group)
with `litecoin-cli`, and then leverage ckb-auth to check the validity of this signature.
See [the docs](./auth.md) for more details.

# Generate signature with openssl

## Install openssl
Openssl is available from most operating systems. Refer to your favorite package manager to install it.

## Creating or importing secp256r1 private key
You may follow the instructions here to import your existing secp256r1 private key.
Or you can generate a new one with
```
openssl ecparam -name prime256v1 -genkey -noout -out private.pem
```


## Obtaining the public key hash
We can create a public key file (`public.pem`) corresponding to the private key file (`private.pem`) with the command
```
openssl ec -in private.pem -pubout -out public.pem
```

To show the detailed information about this public key. We can run
```
openssl ec -text -inform PEM -in public.pem -pubin
```

Below is a sample output

```
read EC key
Public-Key: (256 bit)
pub:
04:1c:cb:e9:1c:07:5f:c7:f4:f0:33:bf:a2:48:db:
8f:cc:d3:56:5d:e9:4b:bf:b1:2f:3c:59:ff:46:c2:
71:bf:83:ce:40:14:c6:88:11:f9:a2:1a:1f:db:2c:
0e:61:13:e0:6d:b7:ca:93:b7:40:4e:78:dc:7c:cd:
5c:a8:9a:4c:a9
ASN1 OID: prime256v1
NIST CURVE: P-256
writing EC key
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEHMvpHAdfx/TwM7+iSNuPzNNWXelL
v7EvPFn/RsJxv4POQBTGiBH5ohof2ywOYRPgbbfKk7dATnjcfM1cqJpMqQ==
-----END PUBLIC KEY-----
```

Here the binary string `04:1c:...:a9` is a binary representation of the public key, where `04` represents uncompressed public key,
i.e. both x and y coordinates are given in the following binary string.
And the public key hash is the blake2b 256 hash of the binary string excluding the first byte `04`.
That is the pubkey hash is just the blake2b 256 hash of `1ccbe91c075fc7f4f033bfa248db8fccd3565de94bbfb12f3c59ff46c271bf83ce4014c68811f9a21a1fdb2c0e6113e06db7ca93b7404e78dc7ccd5ca89a4ca9`.

## Sign the message
To sign the message with hex `29553f9e37fa16e45f1d3e616ac5366f6afd9936477f2d6fc870f49bdf540157`, we can run

```
xxd -r -p <<< 29553f9e37fa16e45f1d3e616ac5366f6afd9936477f2d6fc870f49bdf540157 > message
```
to save this message into file `message` and then run

```
openssl dgst -sha256 -sign private.pem message > signature
```
to save the signature into file `signature`

The final signature field of the ckb transaction should be this signature concatenated with the above public key.
Loading

0 comments on commit c5fc603

Please sign in to comment.