1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include <stdlib.h>
#include "sha3.h"
#define SHA3_BLOCK_SIZE 32
#define CUSTOM_ABORT 1
#define CUSTOM_PRINT_ERR 1
#include "syscall.h"
void custom_abort() { syscall_errno(93, 10, 0, 0, 0, 0, 0); }
int custom_print_err(const char* arg, ...) {
(void)arg;
return 0;
}
#include <secp256k1_static.h>
/*
* We are including secp256k1 implementation directly so gcc can strip
* unused functions. For some unknown reasons, if we link in libsecp256k1.a
* directly, the final binary will include all functions rather than those used.
*/
#include <secp256k1.c>
int char_to_int(char ch) {
if (ch >= '0' && ch <= '9') {
return ch - '0';
}
if (ch >= 'a' && ch <= 'f') {
return ch - 'a' + 10;
}
return -1;
}
int hex_to_bin(char* buf, size_t buf_len, const char* hex) {
int i = 0;
for (; i < buf_len && hex[i * 2] != '\0' && hex[i * 2 + 1] != '\0'; i++) {
int a = char_to_int(hex[i * 2]);
int b = char_to_int(hex[i * 2 + 1]);
if (a < 0 || b < 0) {
return -1;
}
buf[i] = ((a & 0xF) << 4) | (b & 0xF);
}
if (i == buf_len && hex[i * 2] != '\0') {
return -1;
}
return i;
}
#define CHECK_LEN(x) \
if ((x) <= 0) { \
return x; \
}
/*
* Arguments are listed in the following order:
* 0. Program name, ignored here, only preserved for compatibility reason
* 1. Current script hash in hex format, which is 128 bytes. While this program
* cannot verify the hash directly, this ensures the script is include in
* signature calculation
* 2. Other additional parameters that might be included. Notice only ASCII
* characters are included, so binary should be passed as binary format.
* 3. Pubkey in hex format, a maximum of 130 bytes will be processed
* 4. Signature in hex format, a maximum of 512 bytes will be processed
*
* This program will run double sha256 on all arguments excluding pubkey and
* signature(also for simplicity, we are running sha256 on ASCII chars directly,
* not deserialized raw bytes), then it will use sha256 result calculated as the
* message to verify the signature. It returns 0 if the signature works, and
* a non-zero value otherwise.
*
* Note all hex values passed in as arguments must have lower case letters for
* deterministic behavior.
*/
int main(int argc, char* argv[]) {
char buf[256];
int len;
if (argc < 4) {
return -1;
}
secp256k1_context context;
int ret = secp256k1_context_initialize(&context, SECP256K1_CONTEXT_VERIFY);
if (ret == 0) {
return 4;
}
len = hex_to_bin(buf, 65, argv[argc - 2]);
CHECK_LEN(len);
secp256k1_pubkey pubkey;
ret = secp256k1_ec_pubkey_parse(&context, &pubkey, buf, len);
if (ret == 0) {
return 1;
}
len = hex_to_bin(buf, 256, argv[argc - 1]);
CHECK_LEN(len);
secp256k1_ecdsa_signature signature;
secp256k1_ecdsa_signature_parse_der(&context, &signature, buf, len);
if (ret == 0) {
return 3;
}
sha3_ctx_t sha3_ctx;
unsigned char hash[SHA3_BLOCK_SIZE];
sha3_init(&sha3_ctx, SHA3_BLOCK_SIZE);
for (int i = 1; i < argc - 2; i++) {
sha3_update(&sha3_ctx, argv[i], strlen(argv[i]));
}
sha3_final(hash, &sha3_ctx);
sha3_init(&sha3_ctx, SHA3_BLOCK_SIZE);
sha3_update(&sha3_ctx, hash, SHA3_BLOCK_SIZE);
sha3_final(hash, &sha3_ctx);
ret = secp256k1_ecdsa_verify(&context, &signature, hash, &pubkey);
if (ret == 1) {
ret = 0;
} else {
ret = 2;
}
return ret;
}