1 /*
2  * Copyright (c) 2017, [Ribose Inc](https://www.ribose.com).
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24  * POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "ecdsa.h"
28 #include "utils.h"
29 #include <botan/ffi.h>
30 #include <string.h>
31 #include "bn.h"
32 
33 static bool
ecdsa_load_public_key(botan_pubkey_t * pubkey,const pgp_ec_key_t * keydata)34 ecdsa_load_public_key(botan_pubkey_t *pubkey, const pgp_ec_key_t *keydata)
35 {
36     botan_mp_t px = NULL;
37     botan_mp_t py = NULL;
38     bool       res = false;
39 
40     const ec_curve_desc_t *curve = get_curve_desc(keydata->curve);
41     if (!curve) {
42         RNP_LOG("unknown curve");
43         return false;
44     }
45     const size_t curve_order = BITS_TO_BYTES(curve->bitlen);
46 
47     if (!mpi_bytes(&keydata->p) || (keydata->p.mpi[0] != 0x04)) {
48         RNP_LOG(
49           "Failed to load public key: %zu, %02x", mpi_bytes(&keydata->p), keydata->p.mpi[0]);
50         return false;
51     }
52 
53     if (botan_mp_init(&px) || botan_mp_init(&py) ||
54         botan_mp_from_bin(px, &keydata->p.mpi[1], curve_order) ||
55         botan_mp_from_bin(py, &keydata->p.mpi[1 + curve_order], curve_order)) {
56         goto end;
57     }
58 
59     if (!(res = !botan_pubkey_load_ecdsa(pubkey, px, py, curve->botan_name))) {
60         RNP_LOG("failed to load ecdsa public key");
61     }
62 end:
63     botan_mp_destroy(px);
64     botan_mp_destroy(py);
65     return res;
66 }
67 
68 static bool
ecdsa_load_secret_key(botan_privkey_t * seckey,const pgp_ec_key_t * keydata)69 ecdsa_load_secret_key(botan_privkey_t *seckey, const pgp_ec_key_t *keydata)
70 {
71     const ec_curve_desc_t *curve;
72     bignum_t *             x = NULL;
73     bool                   res = false;
74 
75     if (!(curve = get_curve_desc(keydata->curve))) {
76         return false;
77     }
78     if (!(x = mpi2bn(&keydata->x))) {
79         return false;
80     }
81     if (!(res = !botan_privkey_load_ecdsa(seckey, BN_HANDLE_PTR(x), curve->botan_name))) {
82         RNP_LOG("Can't load private key");
83     }
84     bn_free(x);
85     return res;
86 }
87 
88 rnp_result_t
ecdsa_validate_key(rnp::RNG * rng,const pgp_ec_key_t * key,bool secret)89 ecdsa_validate_key(rnp::RNG *rng, const pgp_ec_key_t *key, bool secret)
90 {
91     botan_pubkey_t  bpkey = NULL;
92     botan_privkey_t bskey = NULL;
93     rnp_result_t    ret = RNP_ERROR_BAD_PARAMETERS;
94 
95     if (!ecdsa_load_public_key(&bpkey, key) ||
96         botan_pubkey_check_key(bpkey, rng->handle(), 0)) {
97         goto done;
98     }
99     if (!secret) {
100         ret = RNP_SUCCESS;
101         goto done;
102     }
103 
104     if (!ecdsa_load_secret_key(&bskey, key) ||
105         botan_privkey_check_key(bskey, rng->handle(), 0)) {
106         goto done;
107     }
108     ret = RNP_SUCCESS;
109 done:
110     botan_privkey_destroy(bskey);
111     botan_pubkey_destroy(bpkey);
112     return ret;
113 }
114 
115 static const char *
ecdsa_padding_str_for(pgp_hash_alg_t hash_alg)116 ecdsa_padding_str_for(pgp_hash_alg_t hash_alg)
117 {
118     switch (hash_alg) {
119     case PGP_HASH_MD5:
120         return "Raw(MD5)";
121     case PGP_HASH_SHA1:
122         return "Raw(SHA-1)";
123     case PGP_HASH_RIPEMD:
124         return "Raw(RIPEMD-160)";
125 
126     case PGP_HASH_SHA256:
127         return "Raw(SHA-256)";
128     case PGP_HASH_SHA384:
129         return "Raw(SHA-384)";
130     case PGP_HASH_SHA512:
131         return "Raw(SHA-512)";
132     case PGP_HASH_SHA224:
133         return "Raw(SHA-224)";
134     case PGP_HASH_SHA3_256:
135         return "Raw(SHA3(256))";
136     case PGP_HASH_SHA3_512:
137         return "Raw(SHA3(512))";
138 
139     case PGP_HASH_SM3:
140         return "Raw(SM3)";
141     default:
142         return "Raw";
143     }
144 }
145 
146 rnp_result_t
ecdsa_sign(rnp::RNG * rng,pgp_ec_signature_t * sig,pgp_hash_alg_t hash_alg,const uint8_t * hash,size_t hash_len,const pgp_ec_key_t * key)147 ecdsa_sign(rnp::RNG *          rng,
148            pgp_ec_signature_t *sig,
149            pgp_hash_alg_t      hash_alg,
150            const uint8_t *     hash,
151            size_t              hash_len,
152            const pgp_ec_key_t *key)
153 {
154     botan_pk_op_sign_t     signer = NULL;
155     botan_privkey_t        b_key = NULL;
156     rnp_result_t           ret = RNP_ERROR_GENERIC;
157     uint8_t                out_buf[2 * MAX_CURVE_BYTELEN] = {0};
158     const ec_curve_desc_t *curve = get_curve_desc(key->curve);
159     const char *           padding_str = ecdsa_padding_str_for(hash_alg);
160 
161     if (!curve) {
162         return RNP_ERROR_BAD_PARAMETERS;
163     }
164     const size_t curve_order = BITS_TO_BYTES(curve->bitlen);
165     size_t       sig_len = 2 * curve_order;
166 
167     if (!ecdsa_load_secret_key(&b_key, key)) {
168         RNP_LOG("Can't load private key");
169         goto end;
170     }
171 
172     if (botan_pk_op_sign_create(&signer, b_key, padding_str, 0)) {
173         goto end;
174     }
175 
176     if (botan_pk_op_sign_update(signer, hash, hash_len)) {
177         goto end;
178     }
179 
180     if (botan_pk_op_sign_finish(signer, rng->handle(), out_buf, &sig_len)) {
181         RNP_LOG("Signing failed");
182         goto end;
183     }
184 
185     // Allocate memory and copy results
186     if (mem2mpi(&sig->r, out_buf, curve_order) &&
187         mem2mpi(&sig->s, out_buf + curve_order, curve_order)) {
188         ret = RNP_SUCCESS;
189     }
190 end:
191     botan_privkey_destroy(b_key);
192     botan_pk_op_sign_destroy(signer);
193     return ret;
194 }
195 
196 rnp_result_t
ecdsa_verify(const pgp_ec_signature_t * sig,pgp_hash_alg_t hash_alg,const uint8_t * hash,size_t hash_len,const pgp_ec_key_t * key)197 ecdsa_verify(const pgp_ec_signature_t *sig,
198              pgp_hash_alg_t            hash_alg,
199              const uint8_t *           hash,
200              size_t                    hash_len,
201              const pgp_ec_key_t *      key)
202 {
203     botan_pubkey_t       pub = NULL;
204     botan_pk_op_verify_t verifier = NULL;
205     rnp_result_t         ret = RNP_ERROR_SIGNATURE_INVALID;
206     uint8_t              sign_buf[2 * MAX_CURVE_BYTELEN] = {0};
207     size_t               r_blen, s_blen;
208     const char *         padding_str = ecdsa_padding_str_for(hash_alg);
209 
210     const ec_curve_desc_t *curve = get_curve_desc(key->curve);
211     if (!curve) {
212         RNP_LOG("unknown curve");
213         return RNP_ERROR_BAD_PARAMETERS;
214     }
215     const size_t curve_order = BITS_TO_BYTES(curve->bitlen);
216 
217     if (!ecdsa_load_public_key(&pub, key)) {
218         goto end;
219     }
220 
221     if (botan_pk_op_verify_create(&verifier, pub, padding_str, 0)) {
222         goto end;
223     }
224 
225     if (botan_pk_op_verify_update(verifier, hash, hash_len)) {
226         goto end;
227     }
228 
229     r_blen = mpi_bytes(&sig->r);
230     s_blen = mpi_bytes(&sig->s);
231     if ((r_blen > curve_order) || (s_blen > curve_order) ||
232         (curve_order > MAX_CURVE_BYTELEN)) {
233         ret = RNP_ERROR_BAD_PARAMETERS;
234         goto end;
235     }
236 
237     // Both can't fail
238     mpi2mem(&sig->r, &sign_buf[curve_order - r_blen]);
239     mpi2mem(&sig->s, &sign_buf[curve_order + curve_order - s_blen]);
240 
241     if (!botan_pk_op_verify_finish(verifier, sign_buf, curve_order * 2)) {
242         ret = RNP_SUCCESS;
243     }
244 end:
245     botan_pubkey_destroy(pub);
246     botan_pk_op_verify_destroy(verifier);
247     return ret;
248 }
249 
250 pgp_hash_alg_t
ecdsa_get_min_hash(pgp_curve_t curve)251 ecdsa_get_min_hash(pgp_curve_t curve)
252 {
253     switch (curve) {
254     case PGP_CURVE_NIST_P_256:
255     case PGP_CURVE_BP256:
256     case PGP_CURVE_P256K1:
257         return PGP_HASH_SHA256;
258     case PGP_CURVE_NIST_P_384:
259     case PGP_CURVE_BP384:
260         return PGP_HASH_SHA384;
261     case PGP_CURVE_NIST_P_521:
262     case PGP_CURVE_BP512:
263         return PGP_HASH_SHA512;
264     default:
265         return PGP_HASH_UNKNOWN;
266     }
267 }
268